From 947f72687e79b3134aeeba76e87ac73f7f00a2af Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Mon, 10 Jan 2022 12:14:39 -0800 Subject: [PATCH 0001/1008] fix(gradle): Fix gradle example and docs to work with java 12+ (#703) * fix(gradle): Fix gradle example and docs to work with java 12+ Changes: - Bump gradle version to 7.3.3 - Use `io.freefair.aspectj.post-compile-weaving` in example project - Specify java 11 for sourceCompatibility, targetCompatibility - Bump junit version to 4.13.2 closes #702 * fix: no need to include implementation --- docs/index.md | 3 ++ example/HelloWorldFunction/build.gradle | 32 +++++++------------ .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/HelloWorldFunction/pom.xml | 2 +- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/docs/index.md b/docs/index.md index 115edc566..e25844f52 100644 --- a/docs/index.md +++ b/docs/index.md @@ -118,6 +118,9 @@ For more information about the project and available options refer to this [repo aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' } + + sourceCompatibility = 11 + targetCompatibility = 11 ``` ## Environment variables diff --git a/example/HelloWorldFunction/build.gradle b/example/HelloWorldFunction/build.gradle index c7c774a33..f2447ed2a 100644 --- a/example/HelloWorldFunction/build.gradle +++ b/example/HelloWorldFunction/build.gradle @@ -1,6 +1,6 @@ plugins{ id 'java' - id 'aspectj.AspectjGradlePlugin' version '0.0.7' + id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' } repositories { @@ -8,23 +8,12 @@ repositories { } dependencies { - implementation 'software.amazon.lambda:powertools-tracing:1.10.2' - aspectpath 'software.amazon.lambda:powertools-tracing:1.10.2' - - implementation 'software.amazon.lambda:powertools-logging:1.10.2' - aspectpath 'software.amazon.lambda:powertools-logging:1.10.2' - - implementation 'software.amazon.lambda:powertools-metrics:1.10.2' - aspectpath 'software.amazon.lambda:powertools-metrics:1.10.2' - - implementation 'software.amazon.lambda:powertools-sqs:1.10.2' - aspectpath 'software.amazon.lambda:powertools-sqs:1.10.2' - - implementation 'software.amazon.lambda:powertools-parameters:1.10.2' - aspectpath 'software.amazon.lambda:powertools-parameters:1.10.2' - - implementation 'software.amazon.lambda:powertools-validation:1.10.2' - aspectpath 'software.amazon.lambda:powertools-validation:1.10.2' + aspect 'software.amazon.lambda:powertools-logging:1.10.2' + aspect 'software.amazon.lambda:powertools-tracing:1.10.2' + aspect 'software.amazon.lambda:powertools-metrics:1.10.2' + aspect 'software.amazon.lambda:powertools-sqs:1.10.2' + aspect 'software.amazon.lambda:powertools-parameters:1.10.2' + aspect 'software.amazon.lambda:powertools-validation:1.10.2' implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' implementation 'com.amazonaws:aws-lambda-java-events:3.1.0' @@ -32,5 +21,8 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.16.0' implementation 'org.apache.logging.log4j:log4j-core:2.16.0' - testImplementation 'junit:junit:4.12' -} \ No newline at end of file + testImplementation 'junit:junit:4.13.2' +} + +sourceCompatibility = 11 +targetCompatibility = 11 diff --git a/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties b/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties index 2a563242c..2e6e5897b 100644 --- a/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties +++ b/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/example/HelloWorldFunction/pom.xml b/example/HelloWorldFunction/pom.xml index 5c6000a5f..bdc469eca 100644 --- a/example/HelloWorldFunction/pom.xml +++ b/example/HelloWorldFunction/pom.xml @@ -72,7 +72,7 @@ junit junit - 4.13.1 + 4.13.2 test From f52ed7395624711a035f51ab9472933f929ee1a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 12:10:08 +0100 Subject: [PATCH 0002/1008] build(deps): bump aws.sdk.version from 2.17.107 to 2.17.108 (#706) Bumps `aws.sdk.version` from 2.17.107 to 2.17.108. Updates `software.amazon.awssdk:bom` from 2.17.107 to 2.17.108 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.107...2.17.108) Updates `http-client-spi` from 2.17.107 to 2.17.108 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.107...2.17.108) Updates `url-connection-client` from 2.17.107 to 2.17.108 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 10df15437..7701cde5e 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.107 + 2.17.108 2.10.0 1.1.1 UTF-8 From 73f7bb893917aee26718b7994e714c1c3df7e208 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jan 2022 12:08:50 +0100 Subject: [PATCH 0003/1008] build(deps): bump aws.sdk.version from 2.17.108 to 2.17.109 (#707) Bumps `aws.sdk.version` from 2.17.108 to 2.17.109. Updates `software.amazon.awssdk:bom` from 2.17.108 to 2.17.109 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.108...2.17.109) Updates `http-client-spi` from 2.17.108 to 2.17.109 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.108...2.17.109) Updates `url-connection-client` from 2.17.108 to 2.17.109 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7701cde5e..a58ef4d05 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.108 + 2.17.109 2.10.0 1.1.1 UTF-8 From 8faf29ac7f605267b22ad519816a99ec75e2e8d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 12:08:59 +0100 Subject: [PATCH 0004/1008] build(deps): bump maven-compiler-plugin from 3.8.1 to 3.9.0 (#708) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.1 to 3.9.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.9.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a58ef4d05..5ec6cef83 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ UTF-8 1.2.1 3.11.0 - 3.8.1 + 3.9.0 1.14.0 2.22.2 0.8.7 From 6b61bf0e917d4638f35fa36c68dc3a1f2986686e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 12:14:17 +0100 Subject: [PATCH 0005/1008] build(deps): bump aws.sdk.version from 2.17.109 to 2.17.110 (#709) Bumps `aws.sdk.version` from 2.17.109 to 2.17.110. Updates `software.amazon.awssdk:bom` from 2.17.109 to 2.17.110 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.109...2.17.110) Updates `http-client-spi` from 2.17.109 to 2.17.110 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.109...2.17.110) Updates `url-connection-client` from 2.17.109 to 2.17.110 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5ec6cef83..d710528e4 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.109 + 2.17.110 2.10.0 1.1.1 UTF-8 From 0b1963bb02d25471c5f730f3d25ecdd5b01cadac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jan 2022 12:13:45 +0100 Subject: [PATCH 0006/1008] build(deps): bump aws.sdk.version from 2.17.110 to 2.17.111 (#710) Bumps `aws.sdk.version` from 2.17.110 to 2.17.111. Updates `software.amazon.awssdk:bom` from 2.17.110 to 2.17.111 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.110...2.17.111) Updates `http-client-spi` from 2.17.110 to 2.17.111 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.110...2.17.111) Updates `url-connection-client` from 2.17.110 to 2.17.111 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d710528e4..c8bb9ec60 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.110 + 2.17.111 2.10.0 1.1.1 UTF-8 From acdf8b98a6509fd1a81620f98988b04d7db115a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 12:09:01 +0100 Subject: [PATCH 0007/1008] build(deps): bump aws.sdk.version from 2.17.111 to 2.17.112 (#712) Bumps `aws.sdk.version` from 2.17.111 to 2.17.112. Updates `software.amazon.awssdk:bom` from 2.17.111 to 2.17.112 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.111...2.17.112) Updates `http-client-spi` from 2.17.111 to 2.17.112 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.111...2.17.112) Updates `url-connection-client` from 2.17.111 to 2.17.112 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c8bb9ec60..18cb9ba75 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.111 + 2.17.112 2.10.0 1.1.1 UTF-8 From ba061ca96adf51f96981bbcaf0baaa7ab8c4ab1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jan 2022 12:08:49 +0100 Subject: [PATCH 0008/1008] build(deps): bump aws.sdk.version from 2.17.112 to 2.17.113 (#713) Bumps `aws.sdk.version` from 2.17.112 to 2.17.113. Updates `software.amazon.awssdk:bom` from 2.17.112 to 2.17.113 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.112...2.17.113) Updates `http-client-spi` from 2.17.112 to 2.17.113 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.112...2.17.113) Updates `url-connection-client` from 2.17.112 to 2.17.113 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 18cb9ba75..315fcd65a 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.112 + 2.17.113 2.10.0 1.1.1 UTF-8 From 82555358a6258c4b6b5a78c81b323184190c1e74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 12:10:58 +0100 Subject: [PATCH 0009/1008] build(deps): bump aws.sdk.version from 2.17.113 to 2.17.114 (#714) Bumps `aws.sdk.version` from 2.17.113 to 2.17.114. Updates `software.amazon.awssdk:bom` from 2.17.113 to 2.17.114 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.113...2.17.114) Updates `http-client-spi` from 2.17.113 to 2.17.114 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.113...2.17.114) Updates `url-connection-client` from 2.17.113 to 2.17.114 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 315fcd65a..353aff971 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.113 + 2.17.114 2.10.0 1.1.1 UTF-8 From 18911270da5f24831b40b39f651250ef2e59558e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jan 2022 12:08:34 +0100 Subject: [PATCH 0010/1008] build(deps): bump aws.sdk.version from 2.17.114 to 2.17.115 (#715) Bumps `aws.sdk.version` from 2.17.114 to 2.17.115. Updates `software.amazon.awssdk:bom` from 2.17.114 to 2.17.115 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.114...2.17.115) Updates `http-client-spi` from 2.17.114 to 2.17.115 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.114...2.17.115) Updates `url-connection-client` from 2.17.114 to 2.17.115 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 353aff971..93b2f2646 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.114 + 2.17.115 2.10.0 1.1.1 UTF-8 From 6ec5e7c9be49c0e8c43a4a30effe9df8f8980113 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jan 2022 12:11:07 +0100 Subject: [PATCH 0011/1008] build(deps): bump aws.sdk.version from 2.17.115 to 2.17.116 (#716) Bumps `aws.sdk.version` from 2.17.115 to 2.17.116. Updates `software.amazon.awssdk:bom` from 2.17.115 to 2.17.116 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.115...2.17.116) Updates `http-client-spi` from 2.17.115 to 2.17.116 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.115...2.17.116) Updates `url-connection-client` from 2.17.115 to 2.17.116 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93b2f2646..cd710c141 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.115 + 2.17.116 2.10.0 1.1.1 UTF-8 From a39bcc6433e21e776ab517bb4d5dee56d6f42588 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jan 2022 12:08:11 +0100 Subject: [PATCH 0012/1008] build(deps): bump aws.sdk.version from 2.17.116 to 2.17.117 (#720) Bumps `aws.sdk.version` from 2.17.116 to 2.17.117. Updates `software.amazon.awssdk:bom` from 2.17.116 to 2.17.117 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.116...2.17.117) Updates `http-client-spi` from 2.17.116 to 2.17.117 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.116...2.17.117) Updates `url-connection-client` from 2.17.116 to 2.17.117 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd710c141..2b64e51cd 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.116 + 2.17.117 2.10.0 1.1.1 UTF-8 From 5dbeebaf57f46ffacdd1158953573a7202b2cd87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jan 2022 12:13:16 +0100 Subject: [PATCH 0013/1008] build(deps): bump mockito-core from 4.2.0 to 4.3.0 (#721) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.2.0...v4.3.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b64e51cd..c1a19223e 100644 --- a/pom.xml +++ b/pom.xml @@ -231,7 +231,7 @@ org.mockito mockito-core - 4.2.0 + 4.3.0 test From 0f8c5cb0b0734acc43ff7536df5db21a69c01771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 12:09:04 +0100 Subject: [PATCH 0014/1008] build(deps): bump mockito-core from 4.3.0 to 4.3.1 (#722) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.3.0...v4.3.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1a19223e..d8fd29670 100644 --- a/pom.xml +++ b/pom.xml @@ -231,7 +231,7 @@ org.mockito mockito-core - 4.3.0 + 4.3.1 test From 8a76c3279eedab1f144a0d87ed00a4e14035880d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 13:17:29 +0100 Subject: [PATCH 0015/1008] build(deps): bump aws.sdk.version from 2.17.117 to 2.17.118 (#724) Bumps `aws.sdk.version` from 2.17.117 to 2.17.118. Updates `software.amazon.awssdk:bom` from 2.17.117 to 2.17.118 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.117...2.17.118) Updates `http-client-spi` from 2.17.117 to 2.17.118 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.117...2.17.118) Updates `url-connection-client` from 2.17.117 to 2.17.118 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d8fd29670..1c5e91f6c 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.117 + 2.17.118 2.10.0 1.1.1 UTF-8 From 22607bd568b8cbd7a8578122b85cce0ad8367575 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 13:17:42 +0100 Subject: [PATCH 0016/1008] build(deps): bump mockito-inline from 4.2.0 to 4.3.1 (#723) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.2.0 to 4.3.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.2.0...v4.3.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1c5e91f6c..73bed47e7 100644 --- a/pom.xml +++ b/pom.xml @@ -237,7 +237,7 @@ org.mockito mockito-inline - 4.2.0 + 4.3.1 test From 28766d287cb6d9fb54721401503684fad25f8169 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 13:18:38 +0100 Subject: [PATCH 0017/1008] build(deps): bump json-schema-validator from 1.0.65 to 1.0.66 (#718) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.65 to 1.0.66. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.65...1.0.66) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 00bc6711b..1fd7d0896 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -69,7 +69,7 @@ com.networknt json-schema-validator - 1.0.65 + 1.0.66 From 0235a529095975c5e4b05776cb4b7c9c61d1a1a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jan 2022 12:08:15 +0100 Subject: [PATCH 0018/1008] build(deps): bump aws.sdk.version from 2.17.118 to 2.17.119 (#725) Bumps `aws.sdk.version` from 2.17.118 to 2.17.119. Updates `software.amazon.awssdk:bom` from 2.17.118 to 2.17.119 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.118...2.17.119) Updates `http-client-spi` from 2.17.118 to 2.17.119 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.118...2.17.119) Updates `url-connection-client` from 2.17.118 to 2.17.119 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73bed47e7..56c5bcc56 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.118 + 2.17.119 2.10.0 1.1.1 UTF-8 From 1773879ef62c12b7371917952bd56ab23c1e05dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 12:09:31 +0100 Subject: [PATCH 0019/1008] build(deps): bump aws.sdk.version from 2.17.119 to 2.17.120 (#728) Bumps `aws.sdk.version` from 2.17.119 to 2.17.120. Updates `software.amazon.awssdk:bom` from 2.17.119 to 2.17.120 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.119...2.17.120) Updates `http-client-spi` from 2.17.119 to 2.17.120 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.119...2.17.120) Updates `url-connection-client` from 2.17.119 to 2.17.120 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56c5bcc56..02b02e76b 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.119 + 2.17.120 2.10.0 1.1.1 UTF-8 From 2e98c9ace59cd9ba17989fd410ef5491aab4af9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 12:09:54 +0100 Subject: [PATCH 0020/1008] build(deps): bump aws.sdk.version from 2.17.120 to 2.17.121 (#730) Bumps `aws.sdk.version` from 2.17.120 to 2.17.121. Updates `software.amazon.awssdk:bom` from 2.17.120 to 2.17.121 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.120...2.17.121) Updates `http-client-spi` from 2.17.120 to 2.17.121 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.120...2.17.121) Updates `url-connection-client` from 2.17.120 to 2.17.121 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 02b02e76b..5a29764a9 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.120 + 2.17.121 2.10.0 1.1.1 UTF-8 From 06843fba0347283558e2656457d4a6ca1c2972f5 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Mon, 31 Jan 2022 15:01:07 +0100 Subject: [PATCH 0021/1008] chore: Update readme to refer examples repo --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fff63d89c..2b93427aa 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambd ## Example -See **[example](./example/README.md)** for maven or gradle configurations including all features, and a SAM template with all Powertools env vars. +See **[example](https://github.com/aws-samples/aws-lambda-powertools-examples/tree/main/java)** for example project 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](https://github.com/aws-samples/aws-lambda-powertools-examples/blob/main/CONTRIBUTING.md#security-issue-notifications). ## Credits From 1c834e3fee13d257bebdf68a0ffeeda39b1ef0b2 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Feb 2022 09:28:27 +0100 Subject: [PATCH 0022/1008] fix: Prevent message to be marked as success if failed sending to DLQ (#731) * fix: Prevent message to be marked as success if failed sending to DLQ * docs: IAM permission clarification when using with encrypted SQS --- docs/utilities/batch.md | 6 ++- .../powertools/sqs/internal/BatchContext.java | 33 ++++++++++------ .../SqsMessageBatchProcessorAspectTest.java | 38 +++++++++++++++++-- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index ebb98b08b..1c3586360 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -93,7 +93,11 @@ This utility requires additional permissions to work as expected. Lambda functio 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. -Refer [example project](https://github.com/aws-samples/aws-lambda-powertools-examples/blob/main/java/SqsBatchProcessing/template.yaml#L67) for policy details example. +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 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 index ff9add984..93ad2abc4 100644 --- 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 @@ -6,7 +6,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.IntStream; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -158,17 +159,23 @@ private boolean moveNonRetryableMessagesToDlqIfConfigured(Map { + List sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> { SendMessageBatchResponse sendMessageBatchResponse = client.sendMessageBatch(SendMessageBatchRequest.builder() - .entries(entriesToSend) - .queueUrl(dlqUrl.get()) - .build()); + .entries(entriesToSend) + .queueUrl(dlqUrl.get()) + .build()); + LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); + + return sendMessageBatchResponse; }); - return true; + 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; } @@ -220,17 +227,21 @@ private void deleteMessagesFromQueue(final List messages) { DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); LOG.debug("Response from delete request {}", deleteMessageBatchResponse); + + return deleteMessageBatchResponse; }); } } - private void batchRequest(final List listOFEntries, - final int size, - final Consumer> batchLogic) { - IntStream.range(0, listOFEntries.size()) + private List batchRequest(final List listOFEntries, + final int size, + final Function, R> batchLogic) { + + return IntStream.range(0, listOFEntries.size()) .filter(index -> index % size == 0) .mapToObj(index -> listOFEntries.subList(index, Math.min(index + size, listOFEntries.size()))) - .forEach(batchLogic); + .map(batchLogic) + .collect(Collectors.toList()); } private String url(String queueArn) { 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 index dd10ec1df..a65aa486b 100644 --- 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 @@ -1,10 +1,7 @@ package software.amazon.lambda.powertools.sqs.internal; import java.io.IOException; -import java.time.LocalDateTime; import java.util.HashMap; -import java.util.function.Consumer; - import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; @@ -15,11 +12,13 @@ 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; @@ -30,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.in; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -137,6 +135,38 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { 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 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(); From 16e5d3734b5a60e7c565464a6794faf696f0f807 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Feb 2022 09:28:40 +0100 Subject: [PATCH 0023/1008] chore: move core utilities example to aws-samples/aws-lambda-powertools-examples (#733) --- .../src/main/java/helloworld/App.java | 115 ------------------ .../src/main/java/helloworld/AppStream.java | 25 ---- example/template.yaml | 41 ------- 3 files changed, 181 deletions(-) delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/App.java delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppStream.java diff --git a/example/HelloWorldFunction/src/main/java/helloworld/App.java b/example/HelloWorldFunction/src/main/java/helloworld/App.java deleted file mode 100644 index a371e6c11..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/App.java +++ /dev/null @@ -1,115 +0,0 @@ -package helloworld; - -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 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.AWSXRay; -import com.amazonaws.xray.entities.Entity; -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 software.amazon.lambda.powertools.logging.LoggingUtils; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; -import software.amazon.lambda.powertools.tracing.CaptureMode; -import software.amazon.lambda.powertools.tracing.TracingUtils; -import software.amazon.lambda.powertools.tracing.Tracing; - -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 static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; - -/** - * Handler for requests to Lambda function. - */ -public class App implements RequestHandler { - private final static Logger log = LogManager.getLogger(); - - @Logging(logEvent = true, samplingRate = 0.7) - @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - Map headers = new HashMap<>(); - - headers.put("Content-Type", "application/json"); - headers.put("X-Custom-Header", "application/json"); - - metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); - - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); - - LoggingUtils.appendKey("test", "willBeLogged"); - - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() - .withHeaders(headers); - try { - final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); - log.info(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); - }); - - threadOption1(); - - threadOption2(); - - log.info("After output"); - return response - .withStatusCode(200) - .withBody(output); - } catch (IOException | InterruptedException e) { - return response - .withBody("{}") - .withStatusCode(500); - } - } - - private void threadOption1() throws InterruptedException { - final Entity traceEntity = AWSXRay.getTraceEntity(); - assert traceEntity != null; - traceEntity.run(new Thread(this::log)); - } - - private void threadOption2() throws InterruptedException { - Entity traceEntity = AWSXRay.getTraceEntity(); - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); - anotherThread.start(); - anotherThread.join(); - } - - @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/example/HelloWorldFunction/src/main/java/helloworld/AppStream.java b/example/HelloWorldFunction/src/main/java/helloworld/AppStream.java deleted file mode 100644 index aed048eef..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/AppStream.java +++ /dev/null @@ -1,25 +0,0 @@ -package helloworld; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Map; - -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; - -public class AppStream implements RequestStreamHandler { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); - - System.out.println(map.size()); - } -} diff --git a/example/template.yaml b/example/template.yaml index 435f187cf..e3dddad4a 100644 --- a/example/template.yaml +++ b/example/template.yaml @@ -12,24 +12,6 @@ Globals: Runtime: java11 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: HelloWorldFunction - Handler: helloworld.App::handleRequest - 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: "Payment Service" - POWERTOOLS_LOG_LEVEL: INFO - Tracing: Active - 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 - HelloWorldValidationFunction: 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: @@ -44,23 +26,6 @@ Resources: Path: /hello Method: post - HelloWorldStreamFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: HelloWorldFunction - 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 - HelloWorldParamsFunction: Type: AWS::Serverless::Function Properties: @@ -194,16 +159,10 @@ Outputs: 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 HelloWorldStreamApi: Description: "API Gateway endpoint URL for Prod stage for Hello World stream function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hellostream/" - HelloWorldStreamFunction: - Description: "Hello World Stream Lambda Function ARN" - Value: !GetAtt HelloWorldStreamFunction.Arn HelloWorldParamsApi: Description: "API Gateway endpoint URL for Prod stage for Hello World params function" From 15029c4e0d0797a1d59f11d950a10ea5ee948793 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Feb 2022 10:22:52 +0100 Subject: [PATCH 0024/1008] chore:Prep release 1.10.3 (#734) * chore:prep release 1.10.3 * Changelog update for 1.10.3 Co-authored-by: pankajagrawal16 --- CHANGELOG.md | 12 ++++++++++++ README.md | 6 +++--- example/HelloWorldFunction/build.gradle | 12 ++++++------ example/HelloWorldFunction/pom.xml | 12 ++++++------ mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 38 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a369183e..bddba41fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.10.3] - 2022-02-01 + +### Bug Fixes + +* **SQS Batch processing**: Prevent message to be marked as success if failed sending to DLQ for non retryable exceptions. [#731]https://github.com/awslabs/aws-lambda-powertools-java/pull/731 + +### Documentation + + +* **SQS Batch processing**: Improve [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/batch/#iam-permissions) on IAM premissions required by function when using utility with an encrypted SQS queue with customer managed KMS keys. + + ## [1.10.2] - 2022-01-07 * **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/awslabs/aws-lambda-powertools-java/pull/698) diff --git a/README.md b/README.md index 2b93427aa..111b31af6 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.10.2 + 1.10.3 software.amazon.lambda powertools-logging - 1.10.2 + 1.10.3 software.amazon.lambda powertools-metrics - 1.10.2 + 1.10.3 ... diff --git a/example/HelloWorldFunction/build.gradle b/example/HelloWorldFunction/build.gradle index f2447ed2a..d7b0221d6 100644 --- a/example/HelloWorldFunction/build.gradle +++ b/example/HelloWorldFunction/build.gradle @@ -8,12 +8,12 @@ repositories { } dependencies { - aspect 'software.amazon.lambda:powertools-logging:1.10.2' - aspect 'software.amazon.lambda:powertools-tracing:1.10.2' - aspect 'software.amazon.lambda:powertools-metrics:1.10.2' - aspect 'software.amazon.lambda:powertools-sqs:1.10.2' - aspect 'software.amazon.lambda:powertools-parameters:1.10.2' - aspect 'software.amazon.lambda:powertools-validation:1.10.2' + aspect 'software.amazon.lambda:powertools-logging:1.10.3' + aspect 'software.amazon.lambda:powertools-tracing:1.10.3' + aspect 'software.amazon.lambda:powertools-metrics:1.10.3' + aspect 'software.amazon.lambda:powertools-sqs:1.10.3' + aspect 'software.amazon.lambda:powertools-parameters:1.10.3' + aspect 'software.amazon.lambda:powertools-validation:1.10.3' implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' implementation 'com.amazonaws:aws-lambda-java-events:3.1.0' diff --git a/example/HelloWorldFunction/pom.xml b/example/HelloWorldFunction/pom.xml index bdc469eca..e742c66cf 100644 --- a/example/HelloWorldFunction/pom.xml +++ b/example/HelloWorldFunction/pom.xml @@ -16,32 +16,32 @@ software.amazon.lambda powertools-tracing - 1.10.2 + 1.10.3 software.amazon.lambda powertools-logging - 1.10.2 + 1.10.3 software.amazon.lambda powertools-metrics - 1.10.2 + 1.10.3 software.amazon.lambda powertools-parameters - 1.10.2 + 1.10.3 software.amazon.lambda powertools-validation - 1.10.2 + 1.10.3 software.amazon.lambda powertools-sqs - 1.10.2 + 1.10.3 com.amazonaws diff --git a/mkdocs.yml b/mkdocs.yml index 4e65aa01e..6ed6ec179 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -75,7 +75,7 @@ extra_javascript: extra: powertools: - version: 1.10.2 + version: 1.10.3 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index 5a29764a9..1cecee72e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.10.2 + 1.10.3 pom AWS Lambda Powertools Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index e4b96bc33..9264f446d 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index addb29c51..5c5034c51 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library Core diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 32f40384f..b6e6963ed 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 8e1cd3056..609f0da8c 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 653a9e5a0..ffb982f22 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 powertools-parameters diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 8f73de92c..6bb325856 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 07ac1c969..b8d60b6e3 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d2d7efb30..be476dd48 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 1fd7d0896..ba0723e41 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.2 + 1.10.3 AWS Lambda Powertools Java validation library From 27f9f0204df754d7a9db8a0a2d9513f27503fa63 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Feb 2022 10:47:41 +0100 Subject: [PATCH 0025/1008] chore(docs): typo in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bddba41fa..299716087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### Bug Fixes -* **SQS Batch processing**: Prevent message to be marked as success if failed sending to DLQ for non retryable exceptions. [#731]https://github.com/awslabs/aws-lambda-powertools-java/pull/731 +* **SQS Batch processing**: Prevent message to be marked as success if failed sending to DLQ for non retryable exceptions. [#731](https://github.com/awslabs/aws-lambda-powertools-java/pull/731) ### Documentation From dd9df6da571bee469e3db86d39e6ad99282e93cb Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Feb 2022 12:58:49 +0100 Subject: [PATCH 0026/1008] chore(docs): Clarify test config needed for Tracing module (#735) --- docs/core/tracing.md | 86 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 5c08d5c5a..550f9614c 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -243,3 +243,89 @@ under a subsegment, or you are doing multithreaded programming. Refer examples b 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). + +## Testing your code + +When using `@Tracing` annotation, your Junit test cases needs to be configured to create parent Segment required by [AWS X-Ray SDK for Java](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html). + +Below are two ways in which you can configure your tests. + +#### Configure environment variable on project level (Recommended) + +You can choose to configure environment variable on project level for your test cases run. This is recommended approach as it will avoid the need of configuring each test case specifically. + +Below are examples configuring your maven/gradle projects. You can choose to configure it differently as well as long as you are making sure that environment variable `LAMBDA_TASK_ROOT` is set. This variable is +used internally via AWS X-Ray SDK to configure itself properly for lambda runtime. + +=== "Maven (pom.xml)" + + ```xml hl_lines="4-13" + + ... + + + + org.apache.maven.plugins + maven-surefire-plugin + + + handler + + + + + + + ``` + +=== "Gradle (build.gradle) " + + ```json hl_lines="2-4" + // Configures environment variable to avoid initialization of AWS X-Ray segments for each tests + test { + environment "LAMBDA_TASK_ROOT", "handler" + } + ``` + +#### Configure test cases (Not Recommended) + +You can choose to configure each of your test case instead as well if you choose not to configure environment variable on project level. +Below is an example configuration needed for each test case. + +=== "AppTest.java" + + ```java hl_lines="10 11 12 17 18 19 20 21 22 23 24" + 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() { + // Needed when using sam build --use-container + if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { + AWSXRay.endSubsegment(); + } + + if(null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.endSegment(); + } + } + + @Test + public void successfulResponse() { + // test logic + } + ``` + + + From 16f2c40f01811815022e155d24acfe83a9376201 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Feb 2022 16:16:56 +0100 Subject: [PATCH 0027/1008] chore(docs): FAQ for Kotlin projects --- docs/FAQs.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/FAQs.md b/docs/FAQs.md index 582590740..e2cfa523e 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -25,4 +25,25 @@ To enable in-place weaving feature you need to use following `aspectj-maven-plug -``` \ No newline at end of file +``` + +## How can I use Powertools 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. + +To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` configuration: + +```xml hl_lines="2" + + true + ... + + + software.amazon.lambda + powertools-logging + + + +``` + From 2c7526ce39b30113c7511f7ede45d3c90f847961 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 12:09:38 +0100 Subject: [PATCH 0028/1008] build(deps): bump aws.sdk.version from 2.17.121 to 2.17.122 (#736) Bumps `aws.sdk.version` from 2.17.121 to 2.17.122. Updates `software.amazon.awssdk:bom` from 2.17.121 to 2.17.122 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.121...2.17.122) Updates `http-client-spi` from 2.17.121 to 2.17.122 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.121...2.17.122) Updates `url-connection-client` from 2.17.121 to 2.17.122 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1cecee72e..2bd934d4e 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.121 + 2.17.122 2.10.0 1.1.1 UTF-8 From e8199b3f51d7ab19c188ecf3953af81cf2468994 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 12:16:05 +0100 Subject: [PATCH 0029/1008] build(deps): bump aws.xray.recorder.version from 2.10.0 to 2.11.0 (#737) Bumps `aws.xray.recorder.version` from 2.10.0 to 2.11.0. Updates `aws-xray-recorder-sdk-core` from 2.10.0 to 2.11.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.10.0...v2.11.0) Updates `aws-xray-recorder-sdk-aws-sdk-core` from 2.10.0 to 2.11.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.10.0...v2.11.0) Updates `aws-xray-recorder-sdk-aws-sdk-v2` from 2.10.0 to 2.11.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.10.0...v2.11.0) Updates `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.10.0 to 2.11.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.10.0...v2.11.0) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2bd934d4e..46ae2458b 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 2.13.1 1.9.7 2.17.122 - 2.10.0 + 2.11.0 1.1.1 UTF-8 1.2.1 From 18191ddd36421a944a4ada95ff824cd3cecc3910 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:09:28 +0100 Subject: [PATCH 0030/1008] build(deps): bump aws.sdk.version from 2.17.122 to 2.17.123 (#738) Bumps `aws.sdk.version` from 2.17.122 to 2.17.123. Updates `software.amazon.awssdk:bom` from 2.17.122 to 2.17.123 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.122...2.17.123) Updates `http-client-spi` from 2.17.122 to 2.17.123 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.122...2.17.123) Updates `url-connection-client` from 2.17.122 to 2.17.123 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46ae2458b..a5f3c8672 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.122 + 2.17.123 2.11.0 1.1.1 UTF-8 From e08fc3ca744ea9485bc7bde3c950d3b73bf04ecb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Feb 2022 12:12:15 +0100 Subject: [PATCH 0031/1008] build(deps): bump aws.sdk.version from 2.17.123 to 2.17.124 (#739) Bumps `aws.sdk.version` from 2.17.123 to 2.17.124. Updates `software.amazon.awssdk:bom` from 2.17.123 to 2.17.124 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.123...2.17.124) Updates `http-client-spi` from 2.17.123 to 2.17.124 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.123...2.17.124) Updates `url-connection-client` from 2.17.123 to 2.17.124 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a5f3c8672..0969f11d1 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.123 + 2.17.124 2.11.0 1.1.1 UTF-8 From a5c76c86ff89b759f572e8d4885b8f1d8075ad4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Feb 2022 12:09:04 +0100 Subject: [PATCH 0032/1008] build(deps): bump aws.sdk.version from 2.17.124 to 2.17.125 (#740) Bumps `aws.sdk.version` from 2.17.124 to 2.17.125. Updates `software.amazon.awssdk:bom` from 2.17.124 to 2.17.125 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.124...2.17.125) Updates `http-client-spi` from 2.17.124 to 2.17.125 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.124...2.17.125) Updates `url-connection-client` from 2.17.124 to 2.17.125 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0969f11d1..dc453e069 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.124 + 2.17.125 2.11.0 1.1.1 UTF-8 From a809d67abec9732cd98ff167f7688e6c01d7e287 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Feb 2022 12:09:45 +0100 Subject: [PATCH 0033/1008] build(deps): bump aws.sdk.version from 2.17.125 to 2.17.126 (#741) Bumps `aws.sdk.version` from 2.17.125 to 2.17.126. Updates `software.amazon.awssdk:bom` from 2.17.125 to 2.17.126 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.125...2.17.126) Updates `http-client-spi` from 2.17.125 to 2.17.126 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.125...2.17.126) Updates `url-connection-client` from 2.17.125 to 2.17.126 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc453e069..9ed0dcfc3 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.125 + 2.17.126 2.11.0 1.1.1 UTF-8 From 6d4283a5222f1ab3ea1ea9d7abaed36c959f7c17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Feb 2022 12:44:46 +0100 Subject: [PATCH 0034/1008] build(deps): bump aws.sdk.version from 2.17.126 to 2.17.127 (#742) Bumps `aws.sdk.version` from 2.17.126 to 2.17.127. Updates `software.amazon.awssdk:bom` from 2.17.126 to 2.17.127 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.126...2.17.127) Updates `http-client-spi` from 2.17.126 to 2.17.127 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.126...2.17.127) Updates `url-connection-client` from 2.17.126 to 2.17.127 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ed0dcfc3..ad10e703d 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.126 + 2.17.127 2.11.0 1.1.1 UTF-8 From 390d4aee4d2ee4dcb268041d9b25313a4ae34b0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Feb 2022 13:40:47 +0100 Subject: [PATCH 0035/1008] build(deps): bump maven-javadoc-plugin from 3.3.1 to 3.3.2 (#744) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.1...maven-javadoc-plugin-3.3.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad10e703d..090ab0372 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 0.8.7 2.7 1.6.8 - 3.3.1 + 3.3.2 3.2.1 3.0.1 5.8.2 From a6771515cc4ca495604b32fb46d1517e50691523 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:10:41 +0100 Subject: [PATCH 0036/1008] build(deps): bump aws.sdk.version from 2.17.127 to 2.17.129 (#746) Bumps `aws.sdk.version` from 2.17.127 to 2.17.129. Updates `software.amazon.awssdk:bom` from 2.17.127 to 2.17.129 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.127...2.17.129) Updates `http-client-spi` from 2.17.127 to 2.17.129 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.127...2.17.129) Updates `url-connection-client` from 2.17.127 to 2.17.129 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 090ab0372..c71718c13 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.127 + 2.17.129 2.11.0 1.1.1 UTF-8 From c5f7615bc2093defd255caac46230922ce317a42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:11:06 +0100 Subject: [PATCH 0037/1008] build(deps): bump maven-compiler-plugin from 3.9.0 to 3.10.0 (#748) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.9.0 to 3.10.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.9.0...maven-compiler-plugin-3.10.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c71718c13..bf4822d91 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ UTF-8 1.2.1 3.11.0 - 3.9.0 + 3.10.0 1.14.0 2.22.2 0.8.7 From 11eff9e64b296cc04ac7b459198a91eb24bbea46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:15:49 +0100 Subject: [PATCH 0038/1008] build(deps): bump nexus-staging-maven-plugin from 1.6.8 to 1.6.10 (#747) Bumps nexus-staging-maven-plugin from 1.6.8 to 1.6.10. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf4822d91..fbcaa8850 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 2.22.2 0.8.7 2.7 - 1.6.8 + 1.6.10 3.3.2 3.2.1 3.0.1 From c7a3feac69235aef6ac597e0b84cd2b1aabe0672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Feb 2022 12:09:43 +0100 Subject: [PATCH 0039/1008] build(deps): bump aws.sdk.version from 2.17.129 to 2.17.130 (#749) Bumps `aws.sdk.version` from 2.17.129 to 2.17.130. Updates `software.amazon.awssdk:bom` from 2.17.129 to 2.17.130 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.129...2.17.130) Updates `http-client-spi` from 2.17.129 to 2.17.130 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.129...2.17.130) Updates `url-connection-client` from 2.17.129 to 2.17.130 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fbcaa8850..7ad1765fe 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.129 + 2.17.130 2.11.0 1.1.1 UTF-8 From 3a41ff90b853d77154fad605e8f9dc2472acb7e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Feb 2022 12:08:44 +0100 Subject: [PATCH 0040/1008] build(deps): bump nexus-staging-maven-plugin from 1.6.10 to 1.6.11 (#751) Bumps nexus-staging-maven-plugin from 1.6.10 to 1.6.11. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7ad1765fe..5af81b05b 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 2.22.2 0.8.7 2.7 - 1.6.10 + 1.6.11 3.3.2 3.2.1 3.0.1 From fc95eed98a716f79f57b1bcdf8b3779f9a8513e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Wed, 16 Feb 2022 18:04:12 +0100 Subject: [PATCH 0041/1008] feature: Idempotency module (#717) * Move JSON config to a new utility module Move ObjectMapper and JMESPath config to a new module, to be reused by other powertools modules * Use new utility module for JSON config * Idempotency module * Idempotency module example * Idempotency module added to lambda-powertools * solve JDK 16 build * refactoring to simplify 'around' method * solve javadoc warnings * PR #717: document exceptions * PR #717: move cache to utilities * PR #717: native libs for dynamo & sqlite * PR #717: changes post review * PR #717: add modules to GitHub workflows * PR #717: fix utilities dependency * PR #717: fix utilities dependency * PR #717: fix utilities dependency * PR #717: upgrade utilities and idempotency version to 1.10.3 * PR #717: end to end test with a handler * PR #717: upgrade version in example * PR #717: expiration in seconds instead of ms * PR #717: new test * adding documentation * removing unused dependencies * sqlite dependency for Mac M1 * update doc for sqlite dependency on M1 * change expiration to duration * update doc for utilities * PR #717: rename utilities into serialization * PR #717: move back LRU into idempotency module * PR #717: local cache disabled by default * PR #717: update documentation - add installation guide - sam template indentation - local cache disabled by default - + minors changes --- .github/workflows/build.yml | 4 + .github/workflows/spotbugs.yml | 2 + .gitignore | 1 + docs/media/idempotent_sequence.png | Bin 0 -> 74622 bytes docs/media/idempotent_sequence_exception.png | Bin 0 -> 46647 bytes docs/utilities/idempotency.md | 1006 +++++++++++++++++ docs/utilities/serialization.md | 232 ++++ docs/utilities/validation.md | 139 --- example/HelloWorldFunction/build.gradle | 6 +- example/HelloWorldFunction/pom.xml | 7 +- .../main/java/helloworld/AppIdempotency.java | 84 ++ example/template.yaml | 35 + mkdocs.yml | 2 + pom.xml | 7 + powertools-idempotency/README.md | 14 + powertools-idempotency/pom.xml | 214 ++++ .../powertools/idempotency/Constants.java | 20 + .../powertools/idempotency/Idempotency.java | 108 ++ .../idempotency/IdempotencyConfig.java | 217 ++++ .../idempotency/IdempotencyKey.java | 37 + .../powertools/idempotency/Idempotent.java | 43 + ...IdempotencyAlreadyInProgressException.java | 26 + .../IdempotencyConfigurationException.java | 30 + ...IdempotencyInconsistentStateException.java | 30 + ...IdempotencyItemAlreadyExistsException.java | 29 + .../IdempotencyItemNotFoundException.java | 25 + .../exceptions/IdempotencyKeyException.java | 26 + .../IdempotencyPersistenceLayerException.java | 25 + .../IdempotencyValidationException.java | 32 + .../internal/IdempotencyHandler.java | 161 +++ .../internal/IdempotentAspect.java | 94 ++ .../idempotency/internal/cache/LRUCache.java | 39 + .../persistence/BasePersistenceStore.java | 350 ++++++ .../idempotency/persistence/DataRecord.java | 109 ++ .../persistence/DynamoDBPersistenceStore.java | 357 ++++++ .../persistence/PersistenceStore.java | 54 + .../idempotency/DynamoDBConfig.java | 79 ++ .../idempotency/IdempotencyTest.java | 43 + .../handlers/IdempotencyEnabledFunction.java | 41 + .../handlers/IdempotencyFunction.java | 79 ++ .../handlers/IdempotencyInternalFunction.java | 46 + ...dempotencyInternalFunctionInternalKey.java | 36 + .../IdempotencyInternalFunctionInvalid.java | 41 + .../IdempotencyInternalFunctionVoid.java | 41 + .../IdempotencyWithErrorFunction.java | 33 + .../internal/IdempotencyAspectTest.java | 288 +++++ .../internal/cache/LRUCacheTest.java | 33 + .../powertools/idempotency/model/Basket.java | 55 + .../powertools/idempotency/model/Product.java | 70 ++ .../persistence/BasePersistenceStoreTest.java | 365 ++++++ .../DynamoDBPersistenceStoreTest.java | 278 +++++ .../src/test/resources/apigw_event.json | 62 + .../src/test/resources/apigw_event2.json | 62 + powertools-serialization/pom.xml | 75 ++ .../powertools/utilities/JsonConfig.java | 92 ++ .../utilities}/jmespath/Base64Function.java | 12 +- .../jmespath/Base64GZipFunction.java | 18 +- .../utilities/jmespath/JsonFunction.java | 36 + .../jmespath}/Base64FunctionTest.java | 7 +- .../jmespath}/Base64GZipFunctionTest.java | 7 +- .../utilities/jmespath/JsonFunctionTest.java | 34 + .../src/test/resources/custom_event.json | 12 + .../src/test/resources/custom_event_gzip.json | 12 + .../src/test/resources/custom_event_json.json | 3 + powertools-validation/pom.xml | 4 + .../validation/ValidationConfig.java | 42 +- .../validation/internal/ValidationAspect.java | 4 +- spotbugs-exclude.xml | 16 + 68 files changed, 5395 insertions(+), 196 deletions(-) create mode 100644 docs/media/idempotent_sequence.png create mode 100644 docs/media/idempotent_sequence_exception.png create mode 100644 docs/utilities/idempotency.md create mode 100644 docs/utilities/serialization.md create mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java create mode 100644 powertools-idempotency/README.md create mode 100644 powertools-idempotency/pom.xml create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java create mode 100644 powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java create mode 100644 powertools-idempotency/src/test/resources/apigw_event.json create mode 100644 powertools-idempotency/src/test/resources/apigw_event2.json create mode 100644 powertools-serialization/pom.xml create mode 100644 powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java rename {powertools-validation/src/main/java/software/amazon/lambda/powertools/validation => powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities}/jmespath/Base64Function.java (94%) rename {powertools-validation/src/main/java/software/amazon/lambda/powertools/validation => powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities}/jmespath/Base64GZipFunction.java (92%) create mode 100644 powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java rename {powertools-validation/src/test/java/software/amazon/lambda/powertools/validation => powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath}/Base64FunctionTest.java (77%) rename {powertools-validation/src/test/java/software/amazon/lambda/powertools/validation => powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath}/Base64GZipFunctionTest.java (75%) create mode 100644 powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java create mode 100644 powertools-serialization/src/test/resources/custom_event.json create mode 100644 powertools-serialization/src/test/resources/custom_event_gzip.json create mode 100644 powertools-serialization/src/test/resources/custom_event_json.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e59564080..eea26d905 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,10 +7,12 @@ on: paths: - 'powertools-cloudformation/**' - 'powertools-core/**' + - 'powertools-serialization/**' - 'powertools-logging/**' - 'powertools-sqs/**' - 'powertools-tracing/**' - 'powertools-validation/**' + - 'powertools-idempotency/**' - 'powertools-parameters/**' - 'powertools-metrics/**' - 'powertools-test-suite/**' @@ -22,10 +24,12 @@ on: paths: - 'powertools-cloudformation/**' - 'powertools-core/**' + - 'powertools-serialization/**' - 'powertools-logging/**' - 'powertools-sqs/**' - 'powertools-tracing/**' - 'powertools-validation/**' + - 'powertools-idempotency/**' - 'powertools-parameters/**' - 'powertools-metrics/**' - 'powertools-test-suite/**' diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 8976c5042..7955f4533 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -7,11 +7,13 @@ on: 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' diff --git a/.gitignore b/.gitignore index 20f4c17fa..12a60ce4d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ hs_err_pid* # Maven build target/ +native-libs/ ###################### # IntelliJ diff --git a/docs/media/idempotent_sequence.png b/docs/media/idempotent_sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..92593184abbfb79cc673af07bfbb019bf6c06812 GIT binary patch literal 74622 zcmce;1yq#X_xEkn-5t^?-O{N;hqMyHkb=^s(jW~Y-9z^PBBgW-3?PjlA)V6kobmSl z)j!s=*7L5{<#Gu#b6qF)+55XcdtVc(uBwQIPL6)##tkfGB{|I-H}34+xN#d5^%nRI z)7{nR8#i9xP?nR{b~o8fLvbhUpy=dAL%V~I@43@-JSV1S`FO z3@Tj!F8D_-K={b29T0^L8o5tZ$+Lx=Fi>Y`1U>~hQSwep5TYsW=8-iey2pT zym>!!`|iXe1pV7aZ;$q9*H=q-6cl1lc)Zo#g;$agm?@DSTN6j{WZ<^RLMY4$Vuo7~ z6PM@5D0lCsiF@qN)W6g#GphGFKgv;z8Xb^Uy7lbj&rqYmP&vrKP=kV#1v{DOa~zAG z8P+*qp`xNR_x>Bb9s4rygBLr z-FfZ@06sxEREIn^RuyC?zGOu0E8&uJ^ys>BrRbol@fl;uxpX{pDi4 z^0`kg8&{X-+(sWW9k6BaQQMB&KazU!&;Dkx-Q{0}oGB@5+x z@ZYb!xdp?g&;R{|BVuBH-uiED7HW(zY5eGOx?hNb<690nkx@Ub~r-`W_2%!IHQc#_^nm6JF#D?KVT%b-e?yiKwY_AypXy zP))4Mv-Z$mQw$qNr>hSq_Nu=s*uUQ zgY$mPg33EEs!~R_-?qx|0p@chZawqSq^zt5_fGZmfg93>g_=%kU$vR|S}v;OED#Dk zPojc;^dZ->qLP_OZ952?@j7gYG~9(724f6(@}={mUX2Ya&xHC&wmhe`s{M*P{86Io zcd4|S$Iy3oaJ9LN5C)7>l9A<64?8`O8Rl8ahp(H{fN@hf?*2#UW`4}qhQo4 zyC5Z#k#U9gy$d&P{Me+Hp=;_ZqLPyP_acnKku$tU@gsc*PWtrX;A>RAs{O^;hVt5o z#}__VJ2m9(y^fE}ZdAWoBeai|H>|Po{F=@gNo`yVQL+1%nZJY0*1x1PGhH7oyt?vA z5!8WVhsU;DopEB1*1sgMkL~?E<2>f(T$m+r7E%+W#{n>j7f%BZIZVXS(lztYZTOfo zT{gxEYo@AKDeN`P_<|$G&8&V;HDDMHNg0>gJpbL;PBgX+5Ttz5wOyJUsy7C9kHbhU1&v$H3k-PV*^JFS@GoE-~gt>e72 z4&BJ#BZ3z}+uHoSeIz$#8!VG}1BdI`^tgB&=O#$FvDpv02WxD3JzPSp|LAhJ1OO!2eWF~_d8sVf?49x=9z7v-YR$MiC zwC>fK3~&q^Ls?ntjxR#^NZCq^PWJ{_+n%(y<`V!R=#3E_tDa7bs=oF}97CM(t z*Bw7CkCLP=;Xr>zyoZ&9K{6P@;Xew^;IdJab@4< zH};M*C;B?H`#ZA@m;z)%WqOlUfiW+8<8OXVr_y}xpC-|;e0eOE6D#al?=1g7N=7cB za9mNB4G}qhb=HT|*68_*Ejxf-e*bU{p%OXcFdJqUoFyw(gkJCNBz7_HBLuOVa!&?! zwK-9#@#SrO$sIqWGgP}}Z6K$E6BQrg(-m2YfE7KaV60Jd$1Pm=^1=P^CpD?7?U-}t zJE&Ifi}QtGvYyy_i@x`Gw_h!NQ8#(l_>A;1h867bTDy&om>ZY&`%Hf1vCiHKZ7s1j z*jp$0bgxrWJ>Se>E>$Ea!9#A<;M1$H>S{h`Gtyx3$!e=s+WWTSnAx(SvL(jnp)-#T z7rC2BmseK8#7Q->99#L!yDQ4zkwRDp~|cJI6lAnfS~nb)C^kgsMP`*gL3>tkl!(b#JUFE3BnTcxS!F2EbR60w4 z7|dt)X~uq(qSrAOA5>QeFAd|mH$j}|h&L&4Ia7M8wH3Ry+HNv<_vvSbWBevkuTm2U ztyeucir>se1GBF#5PC}@uYRJ~nLRlEIqcZ_a&PfSB_S+jZX8}fx6UfWb0~f9)at4L zsW)WJ%xAQqQ^k62D?M>o)Tc1^+>*7LkDkzRJygX~!*2--+%g^O$YY}86l%Ms#|xC_Yr$L^(cq7ll4yrch$71 zkQ`u}8Y^gMlZ?8{@DsLR&im{MP(|C8>t;>t19f{;1B+-=GsB9fsXZ5pHM*9?X}k zk#7_SZ+SwgUE2<}>S$D=%6F`GS*-48An^V1?9~XL%Oi>~J)*1Cvu0HChv2<*-@0Rr zW3cYatckeVQHf(wG<$aM?z?bb?R9zdX5YiME2gZ%Bpte!QmhNnTNxgd(A9^N1^gCQ zz@TLizPr+rEbIZ~j*e<}GOf~$bicM|rGD0B%O9}0v6_fWYG+?;CXuGbbLdOu!GJqZ zJ4sL@%!3MW!s*2&FAoz04<0>A)_te*JuzJ|%2yV*lT0}Z*Na8}bcxo-(=j!ECRM`g z&c1e$wvlK2^FCmlQ^U!j26sB?Ar$(iN}t%!*R?)A2^@}W9QK93u=kva0n)P zW$25neu4xGEVRkx9Z8xWrCi4J!4T52Lf(Ixm~S7mJ}-$ve)!3#A9mx z6-Go83W2Ajq!hcqWEjsUOW;`{FmuP3Ld-SPeVd#li(#3Pk1{m~i!B;{5rhe?ND_0? zO@L%Pl-tgxa@9iMw-NkhJVC)IJ|_!O+}t?8GmjM~5}T8sL#M`yty)9hq;Zg?m@TIA zm%=3cd{d0Rm%484pB}6}0JayaUCFb}+Om8L zvS^vmXNxsq^)I`uT=_*T}((x!a!puw}FaFAGx5SSW_!OgploX(8>CHj0%4;DMNdd1lw=ik3HQ;P62=`?gHbTo^4P20HcUCaTtRDf{j-{6 z;$fV0R;_ylD(cDdmH8uZ(yb43z7jgn+IJ<|Fq>$gdWnk9dKmFPxj8=fG(ko>7_L{mD)XsTU8SVc|XQigip%jLN z`OfHCq5d62yR%#A)f-NUy*wRA3Rjxm6x?K=E~rUh3%K3KVIJ0I*;BRlsq>&+D&?XnlBQ z{UG(k^fq(4q#t%Yn{$a8-ydE54(4<6qUD7B>2fb4m#J?}L0W~?SL|o0Y$y%Cr@8<_ z1O4OsPx{EDTjOayjbb;{Bp;pZb=ROLAPlnqQoC!R+@&@KhvY&+di6ugkyNI1R^ugP z@gDHBK39mCW$z8v4?v*d&YMU3*RLUhY1IajEj#czehveti&vA7tm}O*2k1XD0ueD+ zI|h~aT~*e9(gUz67!;!LoSw$x;W z|NA>K*^U*U{6eCRySa(b+E9l&I>>@@^{eOfm^!xkb9jF%s#fc%#1%K@_7q1D^`~oW zU-Ph($OZ>3a$?uNZa!+k+9F5~#9$T{6U$dkEAqP@XR!c#TVs=;<(sc4wmEJywYy8J zkuhGnVfMgheN@5HH;;!G-}tGy8X+6rh=zBP{?y8Vsq6aagoSTjk!j2LZYB*8H}&cG zXE=1H2j{bmSmIW)pkn0XJQX6jjI;+?YGp?HL*WU2qg@}>Unw&P*c;p)+dVoW{J~HN z%?jvnBcTu|8fkFr19m%BsP(0%ht;%sGeyuRt0bZKcdjSc*<>xSm800AQ%)_SbBD(U zWLBtJKIhHhEDZTSY``kl+NW%+B^803k5uU$`52W3yJasS(31p(29Oj1+k2F#!BW0^ zrM%1SacN>C$^G-8q>|0YB&Ln{8AC}0XcsOUy3ywO)~5VAP=g6INgu{$HNzZ*B)t2d zUV)%1Q!tu39Qpf1&Nh(TC;Sm&9TrJN;o;$tS<7@l@;af9wQbHLU-ZJ+g#}tEHc;>m z2_xd3CLiT_^ozb+c~BYfnii5g8sb2TCFye(-0eRR`~|*0FkNZx)tX~qiau!oFJL52 z=!s*dc(qpE;>)gOSW}cksn&M1bB!IyaxH#3a&c2AKsDey@Qm>d#qW^GG=-O$|Gfkq z8(o{J^W)>A^|4#oJRq2M8(^*+Z5c-5kqYj~ZkBBsfi$Fjd;hB@_kb*c`-8@~^mGYB zDOa;Qd=^TliT_PCfWyb4o}s3bXc+$n^688_I``2C$%@?M{;ruMs}m@$=}9(cbE>86OdUkn~Gk z*JpuS0<*zgr1i-=7%!CwDUc6a`l9jRh0H~&;Fd(vCy<9yT6RU&HRFuJAQC^7^H%dt zJvJM5#-){ly*ag6gceJk3q3ZF0*qqmd`EIHMTjDs$7UM@?yb=A*RBcSOsL3&g6UxK z$rP`-Q?dpb5(F{R36zdJFseP_sgtvQ_fC$ay^NU&56QIvELs~3!r8IYEWx&9{wvQX zh7{lbPv(J01m5o_h$?WhC0cX!sd}k9Kd6N)WWgdTf1k2{VN+{; z?>SDNf&@&+@hTeb!mWCi>0iH@k0WtHxhEKzcjmKKyn*@4VXSk@Nxo+-52> zGxPQ98>Xi70K55GslTuhmQH0-J?c$hUuU9az>6jXbn_{&754|*F$GV-#oZ_L5b5F` zybm4>&CmN?p8X1;?ZZM*Mb8f+XooIZ$jZo!LZp=Fe>1b+=_BcJh&)ocFdU7+R|nHD z(9x%=t#HhO%gc9c+TO&q7B1XzN*12{O&4EB%e`Lk`s^ppJw7Gn zy8=8?Qc_&pim?~pbN?PHK!3l7`rpqFT=u?6s+imMQcoNz&G}YOy1ke#&e@IYB7#k{ ze~g+&!ZX+5mJI&Q$j#7kDjjL_+nrY^VW7=}Ols-V(g2fazeHFGb3qR~bAU&iCgP-5QZrfc6t(rikU zb5^Il011j8p*m4+<_F*|J|%TWqYLU1PCnZEW$5R7 zf_0_Cj4OkW_3tmfM?!Oe#FaGtlmGsIhzat#f4z{chaAOUKl~)JG58y;dj|f?+j$~k z%bLFe78Sntok$h`LH?C(CW@f5v$JV`&jYvN=0f3k_VBY}nEV>k-!p1=ka91o zv3}M57$GVml1Il-o2!~;Fa!I0A=;&r1&u$wazhQ}Y`NIS*v`((r2Vxe|5xomc?z{h z8S)5U;2%YXa3x5C-?gt&P#m*bInh1A{b#eYNpfMc zpImGK{C@hBHbC~piw724fc1~n6A5c0Oqx7@eMq7ZawtG#Gx76lZ@TO)%DW`L1u9w5 z!aq_kU;fM6yBM93RAT0xm@c;}aYo@ws_MI-LUB>7N*jP{MuW5kPiY&F>FAxGot>SZ z?@U&sOf7&KLbUt(3_4{IG?7E!%y;J--06pci^1r{)%lKDIf&cE2*SqWIWPKYkO_@J zHPmZtbe6s*@#TxYnLx2IXw4$}+ZIitlU)IcX9qQj7GDVq`=?L8gO4K}K|Rq=9mHY@ zZ3L)R&`XTMbw7Ha?mMZ{CzSE_})Uq5u|AUf-4BbCeWa zKBUBJ$gx7rj0c&{dqgs9w0%R=5}T7<%Zd0= zBA5IEz~iE?eny+M=%q^fnKV{~hdXVIm)=*5%w$=f1Qi_3_^6kJ=`U9Y(V~u^e5qPM zwemwpg0Lvmli7#_4i64ecrDSfnUm$M&zroC#|yO-`4d@0!1mXaw*hEHX@K|V1pK_~ zV{NcCV_wK6WIwgEw#KTNmYPb;X~1x-IUqqT;+#7XUt-#F75RLdhu@?~yKrvMdL5A; z#c>pgm8|<7jcjdeD}sj80FJ9{y$G_+zF1=;qlafd?=LrBU0p#(a%0F;64-P;y@Fn_ z06o`#JAzFUhfpBi&7r@OEF%B{9EY~ zUQ;!;%;M%Z5%br@>DX_cwkd#`_ zU+c2L*nxh>+p}e5WxHC#RJ14BY4J0$2|L-6iDexAfBC|zn$^Zm_wt@}2Wm@6S;0RbiI-9JUn zcJ#AxI2qsbcywJZYX;Nsh;W#M%R1qd*%TZKEQ`1hm}^2|t*t?P&SAKI*HFP?4|8|A zgqZk1vH$^@L7fAqtGMT3!A!CH-XiRS?NpX*F#6YA<+%3BTm_=)@SR58cT5L&$p!5u zV(u#jm_~n3e*gAQEHR_Kta<`If}cN};UQe?0iG45WBG!|ZKY zL&N!ENBF{IonwYlL<8(DH8u4MVm56hZwVzJ6gYT8dOkCCby1^6;r=K% z#Oc7w3b9dG*~34XI}|+hIr|=6A7 zN#@5Z)I|6m3^Ndl#>xfXS_nVs&W4ma-3Pj9)ia*A_ zE0W5s-;|Thw7GE?gaqehl?Aeg-XCX%(G?>p@le=|K343$+Mdym&-Aw5yutGK6a{19 z{98pj#R0&i1@qN1h)ruW*1qyu_I`H@!X$Iunj*r*RnJktiV^j_IQbS__2Lzg26W1P zx;FG)Pa-!>`zo+kb!E61e^K}MjSY5Jxx3H%Kkz_D7#tmU;#swl0ZR@i;U=C0w-EF? z+61c@&it7v5Z2_f@qTDEdPqGK1&6wQLH!N54z;AO(Gr=E!we{~w##(+UF-!5t0Q0# z`J$7zkv$g*T*-Yr8`N4ue@g9k%uuL7LDj9h=&1RJ#fPgGrw5kzh6*(9pNi-giq|OW z;U&QCf|L95<;%EbjNbR0?0nsp*>^x8du&i52z?-yF}y!c%*{ch)Zo--3KX}Axs0AX zj{{}9a(dZNd`4|$;IbbA&pKm#%4$4$a^(N^z1-b@J9k;o6U_b&Z_EzfV+}gOlxfJnNQq`x?YlV18lu0i34kwad!N0_SdPte7M+@P0FRu~@e> zb_cNyTplPg?g}~1QC`cC{wQL1lXPA}b%jn=S?gDG)-)FfhdsDG#xIz+xTh@~?4m6fXLC7kMh>F#g5wzDd?aAvHv34&fVq9}4@U zGUd31l@Ku1G*5Oqa)n4rW$8zDZ)Dm37MM-nX(5l4wAKIXM;7NLLK8B)g}8S_(1END zxfF5V%{+^wYsbIHz4H%|@FEq4RH1(wE}Z@!G-uXt$@%lfKgDm86eLASlJ#nT`H|7< zp&@k_8SmZ24vZRhX=S@#bfMAhlSp;?8L3VWZwZu^(ot~Yladx3O+^4x&zwybu!X6H zP&|9~3~V=Wi)Es8(vua>+CcOgHIX5lKuXYnb`qq_26wWx?H-J0jSCJAp0X9%1jVjq zZ?Hjq;Dj*Mu4e0%n*jKHsb}hwr%JmdAc!0!-QHaw;^ny25mV$#_1F3s@Wjstl53>i z+(Jq#C3!LWU%*s(C563D9w751!j$XF<9Q$k9>uf7@R)bX1fpY+@h){nQ1DjkBDnOc z?lhp>xub;h+0vizF_=cBSqCx4lg!7OJZ{@Y%*^?=-^y6ih~4V)_SL)Ff^oiPNJ<{r zj~2x2pRR)?iaz-K=C-u7^iTmf$y_=`qd-;37qztb1)>wZumS5>Y`+4;1(9;!drny_ zaK1Rh>E+DKB|%bxZ~Bk@do))3TO+(M0lPIA%ZR~mSZ>nfyxe;?3fGaCM*ax60B}sr zd|*IxBCUf#Kc6zc_}(sD-Y_VY-D6~cD%#=DEvX6wgyW4P^qEN zMY@nFWWKf-1rh)qpPer-|R~i8u?Jz>t z_3_f_YAZGHM@Xoy<@$=E@XebyKr6}x(aVP&^56F0a20>Kf_VY7p{LJdnYpL`IjJY3 za8*^+I3@^w`fP(+8{gBS{kO2_SX%7-jeOU$Xn~;FL zQ%o|onF7KmPbHy9BS&FjVS!hY-(r8MN9@&4S_uK0(e6}{gM-ztd5ZhsfS~r>!y;FT z`Za4`C_boXmoDmpo3_p7byp>tCMNe!`ETh-SZq@`L zA(s)UC?5w0hfEgKiLCw5*gZaJ^=n2HCQvhR>3cuA+6XA^o<$C&iMd6OmKlq!RW^IU z6V&3EuwGTo0BH*supK9C34{f1Clvbn`Z5K+j#5Fq1xBh@q^-R5Cu5Cf$%NGqg(n}i z;Yh}(gLLErVe%2MFQ5gH5;uj2O`AnCH>4kPE1AzqwVqY#c|5HN3sG{dL57s(hJxGU^N&PHm0N)9&JwQh@G9DhK_!;HYiv= z`?XQr^d~dLzdH)A#m9g&fYdE(`5q26W{vClk$qCUq~8@GtERZu(PK~ge#c4~k1&x= z=j}bPyejc5Z|)h@JLmBp0P~K1w$K`=l23#VjxabT^<-Pyx@@M6?oQe!0!{|_wh*s_ z705|276ojd;4jw=A^>7ag@bz!=!M0d4p2lm| zD~y5$6JUow*4Ni}cDN5rn>>j$K>=4LibnFiDmzf)?X$DST<3(yy8;`3HM*4(^-xok zaoA?+`}e1DW&#|_Qc|yN6#qnxN^Ib}AB}a%AQT$Y7i~35@Hsw$a6XtS0wl@#fY5wq z>F}8c7;zHs@^Fr#{JpU{ZxE`3(P?k<_Q`-Lz7_|qX@=F$vDW?j1Z_qOKo`%W=jzTN zy+drMVWDNuN)+`52L|etJi+@$Nangd6Y7)5Wi0A2BOF6s3IiNiA3)j{Tp(Pxu5||{ zqhM2h;4~B*tF|>$N}u(y{laHhQ3>b-YAE;btou#!__$hFB4~+wQfcOVP&e<}mnL=$ zqDt;6aHqSNG!Koy_Nd?w0x8C(hemAF?5*EeZNuwFPyhk~@RGd0fFo&(1WxXKI>;Ug zIi6J5O;+{($_9d49d50WB@Xg6^&W7x`?QIKDQ&V3pUlsyEYm#n76xGfxT4DPy1HYK zhsfb;iyi@ak=Rd(i+kfM7zw)s$neN`EpUbI4HBkkdk=mY;1kcN+!En^pUVBed(F=Tqw^m z(@aDUK1Yl*->GRh!jMBoWU$EU8p{L7O7K;U8@G{w- zLSn&?9JJY&?tarwJju`h%d$H0f8Bnl-jMxI^odkWycPu6xBtljkr$E`q!WGp54`w) z{U+x^PnlOY{)tgd08ROu^8Mf7&i|Q?0t^{~{Z!J06sX9ME4Q6F9p>nhAE1dA9DiLe z0#p>Ez4POpAGpBvb?$)Q6=~K`$144?S_5CrR9q@tgsv!<|2tjBA9eXzA_dFIh!#*zlWL*z9 zF$a-D_)aJBiNwp5epBC5`J^cjT0>0of*J75$N?5)$jXFSb~DMLOw6e{&NWT~3K^FK z9L#cejO@idkQ!;Os@@X!KB)kqye$Z;Z3n5AsgP=UFc*Znn-kg41d_or;U|dgxu)s1 zwjV&-@zwyE1vH2I+2L9WmD1wpBY@}V&@lemvd^8qo-aA*D+tWPT%T^+zY zU;I%_fpgv3Bt}I;O9E}fz$cEPgThT**!R(g~r&@TaK1kNk{Xv18B!?FiUJ$yir2#H0vzli`MS_U5=M{vGhy^gqq zo3{9(3d;szYA{dq1NEWNI+P_l2JS|&0q~zjz&sGsMFiOjQOU%5lr9fF_H#w)D)CEU z5gmcy0+D~o@cn&FYNx%$4hIJZ5^j@t>R8eW9*|!sy_JRZMMFZcEAlb`o)Q8?i&YEO z9-^OiB*<;j2r7ZTNxYWMtAoZs2m+ml0U!fX{61b$?yDJi;(>7t^qz$0VK>mt=!IJO zk?S#QKsQ&MzqyS9Sd?bH^D;78Xfc8k#LxoJByw~hrpXfO!aAGMk!mDt{LJURfqn--`7A9Osyr}Ln_^f`j@MQbta{TeAYv> z@Z0O}1iwr|K0J`#F1VFl2NJ3J0VLxQxfGxe>&3{d0f{BxNyZES6PAM{2_(7kg~E80 z$3UwC^JFz?Jb8WdcEB~r(Exw~jOs=rMyP};#47IM}~htu0`9PITRx#eKbD$P;Y zbb<1U5Au3GoGstlV$p-Yd4BL!vM&{Hg=BEyVj-A@K6?m0?&!FIb?fTn2ab_ZE)cZN zpo=}jdp}eY@fIv6z#F zBOg9|s7?r$tj&?m@z(9zhP8H~GC=x|ARcS)lb%Y5i#soM-(VI4G2Wg<{K7y!T2CzT+jgtkrhM5Nxs#p zxm|E4nZN~fTYoFjuU2mYSZs5qUJGVW=pArsL(Hyg2ax#3#{e$2S0)QNavcX!2BIXA zCgPW3UEX89BIrO{l>6ki^AKz!U?(84)!sI{e3$*{1;{qp4d5)Vm`0h}`NV&1d`tXP zCl`tj#5iac7PMi1{@ro97V8Qq8x{-_DT&2v%f0zVUBr~LmmxG7K%qRPm|MRt4iRc} zCt0h*A7FI?oN~P4y&c&$<0YXseGqWu7?DTL>~-?{M`IvH*sbLQ5JQwbu)EMUhUV!1 zjK50yQ^){VWt=cowr7SbhceDmaZ;5)|H_MIJ+8Pv3Y#EICT64&cUNE00FM(`F1?x4 zV*U{7CGj;?#9v%pYXtOj;lA0G(p34{MdN3uX$?9_UeKlFm;s7KpZ~#>EpQQ8G!OyZ z%okT%b@SQ+Kq9h=17T?!a4O9;Mc}FMu!e%y0`=-!v}uEMQCu9sQmXfTQ2-6pPe5** zcnTRFkeGu*3HH4KTwK4yr^|)|tn`4%R_+c1KYz*$7+)`>c_=(gdiV=C{3=P1&|iFE z>#bQ3+7E#t@`L08HlI%sYLcPiy(-zyVi>+9vKiKLpyQyS;icg2;wAxCMx4S1vF+Qk zZ<5X|YievJNGT8!5#=ris?Z1v82h6D!pOj=d`HhjS5qYqRs=2#J1xl zEb3{*G{0tm=u{Cb;OA^t0QCpjPKzMafphp!l9}gA?e5c&yhn*gM@PE8w)`N;tFalw zZ=s6=Foj9S8f3FePV!!np5cCT0n6@^>l4 zTpxQPMGgg029O2-b(_+!B{hbIhezYRdvo*~02dMSfH2F~0*xPb%yu(?QG9Vs&JeyB zM#LVy`a8D)a37mFl3p+nMieJm3L+~$9>d0H;eFFYqV}eAGu~QlUOaNpZH-d&Jg@0ZHH%B+0hbJW+yEuDMQiDU0}Vx_zMz}!P(hBS!0wC zbZ0#CJs_S`@4==Ly#Riumti9z>Va(q6afSN`(C0jaPUkKuqM~7^u^CoE8!bU4&@7t9Ccy@M z1#&jKTH9}`&qIc+r$7KMV9S(w_ymCEzL@*LY-3-)s^ywAdy`$`z1p0>YtgW`cwdYT zXbh&}=6Gp}jcK7qDf?&C6ao7H4?4!ZeKxk&*GIYYp36vqwYaDW0+kUqTn$t;A-V#k z!uOYDm59c_vP@wx<4$@W{=|Eho*e0g)9TV5f(L>CDCz+gLkWGJ9*B}zg@7J0gllTF_+A1yF!Ryrc48s$vLh`ZTxe0I z+bFclA<$iS=C&3VVnEg_GC85LOs5;i_y}fnyJvC6JP$@>m22~a@vnt=(&F2+_UpFJ zg{@|Y!r06Y)C1O(en3UBC{`{!U0B|zAQZg33>|TK9F2Y%H0UCLQzJ;>i33ne zb5NBT2yZ)Njo*y&0hi)uR>Rs78!QTN69Tb>WEJHXFDfV-6gz%&MbQNVQO8Q1gx>j% z%%2KRs^j@%0nQH(p51aioxi?f76f*o4y+RZx;{t(|J!W;DnL={jb>2unRK-yJ7&y7 z3^!SHFKa^Fud6}%*sLJ$!wM^$^~)t#urn+%fNx{*@nLUkK-Ue{v~){FBrdo-yU99_v;=W&Jsx7xdkK* z6iP-)M&Z^r@F-`pB2Xh|O$=iC=kjDSJ}^=2f0O+IoDC3Ral64R?k3oJmO`b0NPqmi z{-b#pc8F|1BM{Ny-!OP00XT~Y4(K`(}rQisu`%5^P?@KMgoV^oP;D1#EtHY zTI(qgec?7^HlX0AZVGVj+wL9!x3vZE_B6f%9>DnM{bM$itsf67D|BP%TEVnl(LZySGMHxiUG6Vnad~KuTqUY&ro7zbQjzAN0J5 zfhwBW36~N;cOZi0%6uO324x)JYl#X;GtC1yE$M<*%-&&VGR_fR3Jz%i_>4QL341(Q zZYp^?#N8?g38%%NVpMsMM8jQ7>=WT70q6xS;c}upXr`WW2DLj-hklS`2puiZ0MF=E zv+5=j&osD|gB;V0ll_K%Ug{P3o+r;LvJjv4_Vee@z-xz6K~`E+q#8qWt%+!-a)F{W zQ&du1(1sdxKevYJ`vUc{-1Yp9E?d+!IgqX24-XI9iB31lTXt`!03;h80ubAW`B}~< z6`3qp$rYdmus`hZdwm6bpLUD+NA%e|K>|fkU6S zNOB;W6z|jO?p5s%8P|?B8^c-@6B$(HCk4<`Wha?eQXFS%$OY)?2Nt%(ufzyW+{dR1 zB3+uhOFaZ9Is_j$o!3Sfm*Pk0iKWUi1CWKFR@=oVYSto3y32{r;ZgyalzG;n7}i)C z&s^SO%`|!th|M+o7^XeFJ%MpvbSDKpW)+;(^?+}3Ivybk!h7Wta%jCC)w)u(7?q!7 z<4IIcyHJ0c5Tf2Ko4aYo#ILQ-exdtZ45WuPE(O+=&R&Ysv_Na_+69IX>O(c(_s@LxRX?R**1FfAy&&{_UN6JQu(bdSWTzQGB)=I!MMi9HKOnx67rV0cMXnsLW986N=TmasU5~%Kw;LE2 z)!~&aEo~7o&-tLL^!%SZ?JwNY9*<(heeOAGvP#K{BxY=mgEMzh1pob=pSOIvhSj*s zX5N&k9!nsI&5%S#Y==iv=zN#=b@{*)5FG%j ziN6y7hl|4$XDJ2nVfe>nL@^DD=;KD8?*XD?MYAH_22?PR-Yp=J&(GK0R;a;h2y1?F zP6gMijff57q7t`an0D?4y@bv;5xv;2(47yfCB{ZIvW9Paxyoi#k*v$^&qL*;(a70& zjOp(f=oIM#(NAZ2*|>Wq3!Crpz-w5y(mcZ7AFg0Nb=oVuj{|(uQ0n5&GoxB0M?c~0xBb-YfBgq!v zWarXg5r#pd8zuYSK+YGrBH$XFUA+N+Rb(q^kP|ls6r_^(52M>e`JXR!p@90I@A}9@Xy@m|R$o59$t$))xrRwF=YH{{QKugg|$ z{PV2<;PWI9uZ`p5*0!z@YyRXBTrymmqZ+7-A0!D@0K6=gGz336UK+(d zsVp@+2E_}xD9|bfh@T_~4yCvRh6Pa5uD#5s|LpWU0ye9VGue|1>u{vA6-wB^CnUUM z=>=fUNo>u3Eb;FfXd;4I-q-kAiqZw8g8tiP3(o&ob$;>zRoDo8q1>|6aSa<(ANLq- z3!x&q0Py|pnY_$|`;49Fv%GglWt+kP01 zYyQX1=L~OJfJT#D_XIG!f8VwUh3Yu{VfexxGq~aJDq$@D*%%y$=Iqsu$>PD+4QQO1 zeU73YwJ5xCoF-v12RjljdA%8ac79qj&NOW8B(#w%D4vd-(eIAb*v`4+T9*wOpe2XT zemJ;ozxym)4MliQt?~51o2@7t#6iV`X)*UT{EAbNQiMG4#h=H~21v7H7+l5rSjh9}eK6yW0(S z8{D{uGo{nxS&b_vOZ0v12iu=cVdRq#Bp+>NJA8A)sxZ5@9bw=?E;h1Kh&1M6c)$o8 zyW+m0XHJnnN}`r1;0#VeLPCjbI#tk1G+Pj<2*XFf^oFc{bPDoWjhj?t(gp2IVOE&>ZSnf~wiRmGa+uD6 zn62jlPd+;Pm4yqn8pKrJKL0t)BH1!d^BEO}mu?RQMxSn?@#W6XAIgW)a=sB97 zA;Lg>D2~}YdiCdUT^UBQQ%Bg!1jZIEw*Bz-9Mu~iW8<`NqCQ&eu<_a_D}fk0q1yc4 zj<&WWPjd&~bK37wC`Zvm5hvmZ^&s-iwS!Snf3j>-#%GqkCenf$JUKs-|2|o8FX z&>fyo*--}1M-Hi0lp0RYedMv*oao(s zocct^;&sK|zDlHw93sn!2~zaDX`eX1Lcxo+^lB67$)wo|s;%Zd*~$b!Td#eR%(n%V z8aw?PBYWee-I8jbwX)@c2Ghb9_p`FvP5I^e+B}aq&^+oLO@%a`PF5v3ZSRvkhl3!% zta_*%L$41^r+r=v{;j*WDZp4WU$soME0&S3wSA|_t0BAN9u^yWI-lRZ@)D%k)t)_u zZIEh&ka2T<9xG8QWIBRkIs}uLdj{CO|I4u=Vl#q-$EjbT_Ci@uy~n47*{?v#2x`b7 zd|$qN0mTSV74~Guhi1ZjsJi^$j-dra%iTs6+9smzjKa0@$3IH}$TP1Y%C6fF+5s@@ z!{<|k>g0o*mwCgX1#DU>Ioq?3q;{neSotna%*8BTKaWoLy;PAk4)I8rc_amAajSfI z{81}Edt&)y@8L*JvX@;BOrAze=`xF zu4qp;+QAecH0nNG8`uF8KJHDZ4sDqVXz|^}7-JLUG!b^XeR4*=djGKuqAc}{jBpJ= z!SQla8eCjlLc##`m)BR9b@Va!Yz!h3(Ef-uNqlvLpdG1*n|5BLw>LfW7l$dr(`cLi z58YNli2QFO+Z1Kd3TECdPo-sbf}DO7eV(s>sbPVuTdb?<-rgp$7oziPztlzlgG2*! zd(;Sl7^GMtKpr9n)f8B(f3YADXEtbac~)Nou>wYzrvXP9S^rAoYkgDg)2nT;W4K|1 zp8#kQa`Zi5Msr&8yZkj4UQ%JwM4#^wo`1OUttEWE#&$JcuhzV)TT-Y$?Oa7c0R%2| zBjdD<(ViMFH>;$gCWY~xJPEH)-;YoBbmUeZ`l#eRUSpHDJ3HJi)M8E)YXvSyCBB^cpXrHghQD&x-F&{F*KC$wmL?;$pl|dEJP)9z;yPlCo08#=uT=BcYBZB3mL_ z*RNH##F}ev;qdTP+z2~cbubKk`3#S*EPGMeCUDB}C{<=Mh;3f(c1D_L6@6UgG{MCb3=rK^^Q z5^lYLpJ|a2j#gPhvKAk&kTn)*=S|8Y!dBs+etAV8Cxc-77NVJaM6TbG__W|%kNBzn z&#mjr9moK9>@tZnCEu0g+0@HA4Fvl6UYDz7pTgCfAD74M)>J>4*#nLHI zgfanTj8diCH~%UA0DKz|0<29*d$qYDiba7oYq9J6Ze3oCds>zEDTmum zvw^JA_FRr4+E(*IO!;?Wp{DdD4i5f9pKcuXs){F-n5wEwr|YbnLs{un*bi8^;yk{X zBt5&j2x1bmo>Go0N}BU+0?%c?2kUJ6=)LQqbuRztxOq=nH!GT@BI4J7=C^SS!#Ad;2B%H=u2uYEF zeXEYIfl0U}z-rtV;sH%n;x1n*@?Rg!g+&x{MP$MvzCTIAmH7Rz*U|l(0$ZmC{+fBt zf$H()mSJuJ%ljhg9Juh^lM`+H3_&yu3Qox{MX?HI9jpb4B4`8+dZo!!==vgFr$EPn z9zzjJw~g`cy9MCMR2KgAbcq-~5ee~`lI~U<$#>#+7|`PnfT+?fwTa<2CAXw^l}m-Pf1K@s?p0CV`0|HIi^KvlJUegBFg zAkqo~0+I>{2uOE#DV-7m2Sh*#5do1dDFNwJx({8_p>zrgQqn1*KEUJU3-)0t%ti1^DDx? z29DmCH!VIMJMeh5l*hyR#qpMm%8yjORt!I%&Y1gU3k3s6Fgv{>&Jj0UzkNLO+Nk0Y zYiy28`nwP!RM-(fh03uhro!z*GT`t%c}JWb&t2x3r0(&gVFCkpCRJlUVh!lej&A4x4?*7@Lrxpe#ouCx{(O%|(s zDyHg9IgFmI4d_^pmcK0W+ip4baUs*qCQnI#jO$hxSlIlEG z&~1HYi{bdD)k4oJjs@7nwDPUaE{L4gy2y~Ai}Qt28jxB&IoQz^IUYLuSoYn)LAi9N z?3Ed_;{#_k<{RTe2=ws1b{W|{h8sffd6H|h> zKNmGNQL8H>vG(q|Ta!E{gCvAWOF?7aduGH9)DLsCdJDQz?hHBgr%7A-G2Y7w@^kN} zly!XxkOb|@qW-Uq^9TEEUhhQR*F4pIOC^GS!otxQj&?bEN;RzECGI7Y9ewUxO;h@F zR;C2r81ON`2fHE@4q2r6;X1qZ#RGN@Hi&^+hUusw&Qf}veUS3{&gV;EpU+`LlX7Hy zeu<7VkKtan@M>x^fwy1g8Ioijmfn=d)HNYAAfv9TWol-c1(#82%VKOC|f8G)n zSH5z-PU#W&(F~l7cj+K>oZ6bTHP0QeJmC2E3)<|jhg_Z#O6zy9`J;=_MWe(BrfVzwpsKeZlr$fcp=?KAjk(D$PRn`CmLEH`p*HZ7npNs!bl zhf-)XSPc2aSJ&S@{q-AQc>gZPXG2JI?04M}tr9C*aeE(f z`^Q&5IkMY*sr|IQFO|-94O>RgmAlI}tu6ga6(^boRLCTAcLq8xs-AZ>sJ$8=bR}p{ z@gecBmIYBNLPXcP|KSp5zwZC`L(;>@8Yjo5n-1G^%i|eYw_e5VdEw<4?{Cywzf%fy z4obug2znPH<;uWaI$thFXFi*MmCfREjVt41NF2AaU7q5fa?w>k;Osyw>A0r5`hjhQ z`FqPuRa%HR4HIYiudk;lCF-V21)(uz79>M>nyzg*K3S)LL!oQ96!4|)@`kh$`3+u8 z2D+_gRC3^6GjOCj!Y8lV;Zo`=$8$sqXZTeGj?nF|im(-yG(y7v#nXDXFPn!!Y|EW>zPK!Sy%7AhhvY`U zq+7k4BB~4FKZlo;QV`+eIa!>B3g>Ou{)OH6rDJpMti+8>H3%tl-g~FsF}t3W=XOX_ zVT%`&&2B@VE%DOpaI`R)JiDNvCtvppAS{cvQ%cQ;r6s6u#9>+h^kpPBqa8%VLqgmn2t*uyxX<49Fc>C1J z6FMC@oW7z6n(2!Bc$qO`GP*0BEX+e$Is8G>+_~rd9bMZHou$en2Bjx2L9|)^$N*F3 zbv7)BmUSW5=vsSI?HJZpyUXgkU!1YiwjLyb+5{y6kM0P|i}G2TJ@`3b*X4tqd2g~A zPW3tyCvtVmRtLQFaus70O`P^;T0`s?69ohUGqQSQ9nN1CHdlOLPaMVcAY?gYo#@8F zeWS}S(;M&=R@NLhaZRvs4b-sZ>{GZtco2~B`e992Ac@{�W*NJiVcf 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 literal 0 HcmV?d00001 diff --git a/docs/media/idempotent_sequence_exception.png b/docs/media/idempotent_sequence_exception.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf065993dd0ca3818941e1e80af17f4f7ed56f7 GIT binary patch literal 46647 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 literal 0 HcmV?d00001 diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md new file mode 100644 index 000000000..56b9acb78 --- /dev/null +++ b/docs/utilities/idempotency.md @@ -0,0 +1,1006 @@ +--- +title: Idempotency +description: Utility +--- + +The idempotency utility provides a simple solution to convert your Lambda functions into idempotent operations which +are safe to retry. + +## Terminology + +The property of idempotency means that an operation does not cause additional side effects if it is called more than +once with the same input parameters. + +**Idempotent operations will return the same result when they are called multiple +times with the same parameters**. This makes idempotent operations safe to retry. [Read more](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) about idempotency. + +**Idempotency key** is a hash representation of either the entire event or a specific configured subset of the event, and invocation results are **JSON serialized** and stored in your persistence storage layer. + +## Key features + +* Prevent Lambda handler function from executing more than once on the same event payload during a time window +* Ensure Lambda handler returns the same result when called with the same payload +* Select a subset of the event as the idempotency key using JMESPath expressions +* Set a time window in which records with the same payload should be considered duplicates + +## Getting started + +### Installation +=== "Maven" + ```xml hl_lines="3-7 24-27" + + ... + + software.amazon.lambda + powertools-idempotency + {{ powertools.version }} + + ... + + + + + + ... + + org.codehaus.mojo + aspectj-maven-plugin + 1.14.0 + + 1.8 + 1.8 + 1.8 + + + software.amazon.lambda + powertools-idempotency + + ... + + + + + + compile + + + + + ... + + + ``` + +### 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. + +**Default table configuration** + +If you're not [changing the default configuration for the DynamoDB persistence layer](#dynamodbpersistencestore), this is the expected default configuration: + +| Configuration | Value | Notes | +|--------------------|--------------|-------------------------------------------------------------------------------------| +| Partition key | `id` | | +| TTL attribute name | `expiration` | This can only be configured after your table is created if you're using AWS Console | + +!!! Tip "Tip: You can share a single state table for all functions" + You can reuse the same DynamoDB table to store idempotency state. We add your function name in addition to the idempotency key as a hash key. + +```yaml hl_lines="5-13 21-23 26" title="AWS Serverless Application Model (SAM) example" +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: Function + Handler: helloworld.App::handleRequest + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref IdempotencyTable + Environment: + Variables: + IDEMPOTENCY_TABLE: !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). + 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 + estimate the cost. + +### Idempotent annotation + +You can quickly start by initializing the `DynamoDBPersistenceStore` and using it with the `@Idempotent` annotation on your Lambda handler. + +!!! warning "Important" + Initialization and configuration of the `DynamoDBPersistenceStore` must be performed outside the handler, preferably in the constructor. + +=== "App.java" + + ```java hl_lines="5-9 12 19" + public class App implements RequestHandler { + + 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(); + } + + @Idempotent + public SubscriptionResult handleRequest(final Subscription event, final Context context) { + SubscriptionPayment payment = createSubscriptionPayment( + event.getUsername(), + event.getProductId() + ); + + return new SubscriptionResult(payment.getId(), "success", 200); + } + } + + ``` + +=== "Example event" + + ```json + { + "username": "xyz", + "product_id": "123456789" + } + ``` + +#### Idempotent annotation on another method + +You can use the `@Idempotent` annotation for any synchronous Java function, not only the `handleRequest` one. + +When using `@Idempotent` annotation on another method, you must tell which parameter in the method signature has the data we should use: + + - 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. + +!!! info "The parameter must be serializable in JSON. We use Jackson internally to (de)serialize objects" + +=== "AppSqsEvent.java" + + 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 { + + public AppSqsEvent() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build() + ).withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("messageId") // see Choosing a payload subset section + .build() + ).configure(); + } + + @Override + @SqsBatch(SampleMessageHandler.class) + public String handleRequest(SQSEvent input, Context context) { + dummy("hello", "world"); + return "{\"statusCode\": 200}"; + } + + @Idempotent + private String dummy(String argOne, @IdempotencyKey String argTwo) { + return "something"; + } + + public static class SampleMessageHandler implements SqsMessageHandler { + @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; + } + } + } + ``` + +=== "Batch event" + + ```json hl_lines="4" + { + "Records": [ + { + "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", + "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", + "body": "Test message.", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1545082649183", + "SenderId": "AIDAIENQZJOLO23YVJ4VO", + "ApproximateFirstReceiveTimestamp": "1545082649185" + }, + "messageAttributes": { + "testAttr": { + "stringValue": "100", + "binaryValue": "base64Str", + "dataType": "Number" + } + }, + "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue", + "awsRegion": "us-east-2" + } + ] + } + ``` + +### Choosing a payload subset for idempotency + +!!! tip "Tip: Dealing with always changing payloads" + When dealing with an elaborate payload (API Gateway request for example), where parts of the payload always change, you should configure the **`EventKeyJMESPath`**. + +Use [`IdempotencyConfig`](#customizing-the-default-behavior) to instruct the Idempotent annotation to only use a portion of your payload to verify whether a request is idempotent, and therefore it should not be retried. + +> **Payment scenario** + +In this example, we have a Lambda handler that creates a payment for a user subscribing to a product. We want to ensure that we don't accidentally charge our customer by subscribing them more than once. + +Imagine the function executes successfully, but the client never receives the response due to a connection issue. It is safe to retry in this instance, as the idempotent decorator will return a previously saved response. + +!!! warning "Warning: Idempotency for JSON payloads" + The payload extracted by the `EventKeyJMESPath` is treated as a string by default, so will be sensitive to differences in whitespace even when the JSON payload itself is identical. + + To alter this behaviour, you can use the [JMESPath built-in function](utilities.md#powertools_json-function) `powertools_json()` to treat the payload as a JSON object rather than a string. + +=== "PaymentFunction.java" + + ```java hl_lines="5-7 16 29-31" + public class PaymentFunction implements RequestHandler { + + public PaymentFunction() { + Idempotency.config() + .withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body)") + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + @Idempotent + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent event, final Context context) { + 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" + { + "version":"2.0", + "body":"{\"username\":\"xyz\",\"productId\":\"123456789\"}", + "routeKey":"ANY /createpayment", + "rawPath":"/createpayment", + "rawQueryString":"", + "headers": { + "Header1": "value1", + "Header2": "value2" + }, + "requestContext":{ + "accountId":"123456789012", + "apiId":"api-id", + "domainName":"id.execute-api.us-east-1.amazonaws.com", + "domainPrefix":"id", + "http":{ + "method":"POST", + "path":"/createpayment", + "protocol":"HTTP/1.1", + "sourceIp":"ip", + "userAgent":"agent" + }, + "requestId":"id", + "routeKey":"ANY /createpayment", + "stage":"$default", + "time":"10/Feb/2021:13:40:43 +0000", + "timeEpoch":1612964443723 + }, + "isBase64Encoded":false + } + ``` + + +### Idempotency request flow + +This sequence diagram shows an example flow of what happens in the payment scenario: + +![Idempotent sequence](../media/idempotent_sequence.png) + +The client was successful in receiving the result after the retry. Since the Lambda handler was only executed once, our customer hasn't been charged twice. + +!!! note + Bear in mind that the entire Lambda handler is treated as a single idempotent operation. If your Lambda handler can cause multiple side effects, consider splitting it into separate functions. + +### 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**. +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. + +![Idempotent sequence exception](../media/idempotent_sequence_exception.png) + +If an Exception is raised _outside_ the scope of a decorated method and after your method has been called, the persistent record will not be affected. In this case, idempotency will be maintained for your decorated function. Example: + +```java hl_lines="2-4 8-10" title="Exception not affecting idempotency record sample" + public SubscriptionResult handleRequest(final Subscription event, final Context context) { + // If an exception is thrown here, no idempotent record will ever get created as the + // idempotent function does not get called + doSomeStuff(); + + result = idempotentMethod(event); + + // This exception will not cause the idempotent record to be deleted, since it + // happens after the decorated function has been successfully called + throw new Exception(); + } + + @Idempotent + private String idempotentMethod(final Subscription subscription) { + // perform some operation with no exception thrown + } +``` + +!!! warning + **We will throw an `IdempotencyPersistenceLayerException`** if any of the calls to the persistence layer fail unexpectedly. + + As this happens outside the scope of your decorated function, you are not able to catch it. + +### Persistence stores + +#### DynamoDBPersistenceStore + +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" +DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .withKeyAttr("idempotency_key") + .withExpiryAttr("expires_at") + .withStatusAttr("current_status") + .withDataAttr("result_data") + .withValidationAttr("validation_key") + .build() +``` + +When using DynamoDB as a persistence layer, you can alter the attribute names by passing these parameters when initializing the persistence layer: + +| Parameter | Required | Default | Description | +|--------------------|----------|--------------------------------------|--------------------------------------------------------------------------------------------------------| +| **TableName** | Y | | Table name to store state | +| **KeyAttr** | | `id` | Partition key of the table. Hashed representation of the payload (unless **SortKeyAttr** is specified) | +| **ExpiryAttr** | | `expiration` | Unix timestamp of when record expires | +| **StatusAttr** | | `status` | Stores status of the Lambda execution during and after invocation | +| **DataAttr** | | `data` | Stores results of successfully idempotent methods | +| **ValidationAttr** | | `validation` | Hashed representation of the parts of the event used for validation | +| **SortKeyAttr** | | | Sort key of the table (if table is configured with a sort key). | +| **StaticPkValue** | | `idempotency#{LAMBDA_FUNCTION_NAME}` | Static value to use as the partition key. Only used when **SortKeyAttr** is set. | + +## Advanced + +### Customizing the default behavior + +Idempotency behavior can be further configured with **`IdempotencyConfig`** using a builder: + +```java hl_lines="2-8" title="Customizing IdempotencyConfig" +IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .withPayloadValidationJMESPath("paymentId") + .withThrowOnNoIdempotencyKey(true) + .withExpiration(Duration.of(5, ChronoUnit.MINUTES)) + .withUseLocalCache(true) + .withLocalCacheMaxItems(432) + .withHashFunction("SHA-256") + .build() +``` + +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) | +| **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 | +| **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, ...) | + +These features are detailed below. + +### Handling concurrent executions with the same payload + +This utility will throw an **`IdempotencyAlreadyInProgressException`** if we receive **multiple invocations with the same payload while the first invocation hasn't completed yet**. + +!!! info + If you receive `IdempotencyAlreadyInProgressException`, you can safely retry the operation. + +This is a locking mechanism for correctness. Since we don't know the result from the first invocation yet, we can't safely allow another concurrent execution. + +### Using in-memory cache + +**By default, in-memory local caching is disabled**, to avoid using memory in an unpredictable way. + +!!! warning Memory configuration of your function + Be sure to configure the Lambda memory according to the number of records and the potential size of each record. + +You can enable it as seen before with: +```java title="Enable local cache" + IdempotencyConfig.builder() + .withUseLocalCache(true) + .build() +``` +When enabled, we cache a maximum of 256 records in each Lambda execution environment - You can change it with the **`LocalCacheMaxItems`** parameter. + +!!! note "Note: This in-memory cache is local to each Lambda execution environment" + This means it will be effective in cases where your function's concurrency is low in comparison to the number of "retry" invocations with the same payload, because cache might be empty. + + +### Expiring idempotency records + +!!! note + By default, we expire idempotency records after **an hour** (3600 seconds). + +In most cases, it is not desirable to store the idempotency records forever. Rather, you want to guarantee that the same payload won't be executed within a period of time. + +You can change this window with the **`ExpirationInSeconds`** parameter: +```java title="Customizing expiration time" +IdempotencyConfig.builder() + .withExpiration(Duration.of(5, ChronoUnit.MINUTES)) + .build() +``` + +Records older than 5 minutes will be marked as expired, and the Lambda handler will be executed normally even if it is invoked with a matching payload. + +!!! note "Note: DynamoDB time-to-live field" + This utility uses **`expiration`** as the TTL field in DynamoDB, as [demonstrated in the SAM example earlier](#required-resources). + +### Payload validation + +!!! question "Question: What if your function is invoked with the same payload except some outer parameters have changed?" + Example: A payment transaction for a given productID was requested twice for the same customer, **however the amount to be paid has changed in the second transaction**. + +By default, we will return the same result as it returned before, however in this instance it may be misleading; we provide a fail fast payload validation to address this edge case. + +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" + + ```java hl_lines="8 13 20 26" + public App() { + Idempotency.config() + .withPersistenceStore(DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("[userDetail, productId]") + .withPayloadValidationJMESPath("amount") + .build()) + .configure(); + } + + @Idempotent + public SubscriptionResult handleRequest(final Subscription input, final Context context) { + // 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" + { + "userDetail": { + "username": "User1", + "user_email": "user@example.com" + }, + "productId": 1500, + "charge_type": "subscription", + "amount": 500 + } + ``` + +=== "Example Event 2" + + ```json hl_lines="8" + { + "userDetail": { + "username": "User1", + "user_email": "user@example.com" + }, + "productId": 1500, + "charge_type": "subscription", + "amount": 1 + } + ``` + +In this example, the **`userDetail`** and **`productId`** keys are used as the payload to generate the idempotency key, as per **`EventKeyJMESPath`** parameter. + +!!! note + If we try to send the same request but with a different amount, we will raise **`IdempotencyValidationException`**. + +Without payload validation, we would have returned the same result as we did for the initial request. Since we're also returning an amount in the response, this could be quite confusing for the client. + +By using **`withPayloadValidationJMESPath("amount")`**, we prevent this potentially confusing behavior and instead throw an Exception. + +### Making idempotency key required + +If you want to enforce that an idempotency key is required, you can set **`ThrowOnNoIdempotencyKey`** to `True`. + +This means that we will throw **`IdempotencyKeyException`** if the evaluation of **`EventKeyJMESPath`** is `null`. + +=== "App.java" + + ```java hl_lines="9-10 13" + 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(); + } + + @Idempotent + public OrderResult handleRequest(final Order input, final Context context) { + // ... + } + ``` + +=== "Success Event" + + ```json hl_lines="3 6" + { + "user": { + "uid": "BB0D045C-8878-40C8-889E-38B3CB0A61B1", + "name": "Foo" + }, + "orderId": 10000 + } + ``` + +=== "Failure Event" + + Notice that `orderId` is now accidentally within `user` key + + ```json hl_lines="3 5" + { + "user": { + "uid": "DE0D000E-1234-10D1-991E-EAC1DD1D52C8", + "name": "Joe Bloggs", + "orderId": 10000 + }, + } + ``` + +### Customizing DynamoDB configuration + +When creating the `DynamoDBPersistenceStore`, you can set a custom [`DynamoDbClient`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) if you need to customize the configuration: + +=== "Custom DynamoDbClient with X-Ray interceptor" + + ```java hl_lines="2-8 13" + public App() { + DynamoDbClient customClient = DynamoDbClient.builder() + .region(Region.US_WEST_2) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .addExecutionInterceptor(new TracingInterceptor()) + .build() + ) + .build(); + + Idempotency.config().withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .withDynamoDbClient(customClient) + .build() + ).configure(); + } + ``` + +!!! info "Default configuration is the following:" + + ```java + DynamoDbClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv(AWS_REGION_ENV))) + .build(); + ``` + +### Using a DynamoDB table with a composite primary key + +When using a composite primary key table (hash+range key), use `SortKeyAttr` parameter when initializing your persistence store. + +With this setting, we will save the idempotency key in the sort key instead of the primary key. By default, the primary key will now be set to `idempotency#{LAMBDA_FUNCTION_NAME}`. + +You can optionally set a static value for the partition key using the `StaticPkValue` parameter. + +```java hl_lines="5" title="Reusing a DynamoDB table that uses a composite primary key" +Idempotency.config().withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .withSortKeyAttr("sort_key") + .build()) + .configure(); +``` + +Data would then be stored in DynamoDB like this: + +| id | sort_key | expiration | status | data | +|------------------------------|----------------------------------|------------|-------------|--------------------------------------| +| idempotency#MyLambdaFunction | 1e956ef7da78d0cb890be999aecc0c9e | 1636549553 | COMPLETED | {"id": 12391, "message": "success"} | +| idempotency#MyLambdaFunction | 2b2cdb5f86361e97b4383087c1ffdf27 | 1636549571 | COMPLETED | {"id": 527212, "message": "success"} | +| idempotency#MyLambdaFunction | f091d2527ad1c78f05d54cc3f363be80 | 1636549585 | IN_PROGRESS | | + +### Bring your own persistent store + +This utility provides an abstract base class, so that you can implement your choice of persistent storage layer. + +You can extend the `BasePersistenceStore` class and implement the abstract methods `getRecord`, `putRecord`, +`updateRecord` and `deleteRecord`. You can have a look at [`DynamoDBPersistenceStore`](https://github.com/awslabs/aws-lambda-powertools-java/blob/master/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java) as an implementation reference. + +!!! danger + Pay attention to the documentation for each method - you may need to perform additional checks inside these methods to ensure the idempotency guarantees remain intact. + + 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. + +## Compatibility with other utilities + +### Validation utility + +The idempotency utility can be used with the `@Validation` annotation from the [validation module](validation.md). Ensure that idempotency is the innermost annotation. + +```java hl_lines="1 2" title="Using Idempotency with JSONSchema Validation utility" +@Validation(inboundSchema = "classpath:/schema_in.json") +@Idempotent +public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + // ... +} +``` + +!!! tip "Tip: JMESPath Powertools functions are also available" + Built-in functions like `powertools_json`, `powertools_base64`, `powertools_base64_gzip` are also available to use in this utility. See [JMESPath Powertools functions](serialization.md) + + +## Testing your code + +The idempotency utility provides several routes to test your code. + +### Disabling the idempotency utility +When testing your code, you may wish to disable the idempotency logic altogether and focus on testing your business logic. To do this, you can set the environment variable `POWERTOOLS_IDEMPOTENCY_DISABLED` to true. +If you prefer setting this for specific tests, and are using JUnit 5, you can use [junit-pioneer](https://junit-pioneer.org/docs/environment-variables/) library: + +=== "MyFunctionTest.java" + + ```java hl_lines="2" + @Test + @SetEnvironmentVariable(key = Constants.IDEMPOTENCY_DISABLED_ENV, value = "true") + public void testIdempotencyDisabled_shouldJustRunTheFunction() { + MyFunction func = new MyFunction(); + func.handleRequest(someInput, mockedContext); + } + ``` + +You can also disable the idempotency for all tests using `maven-surefire-plugin` and adding the environment variable: + +=== "pom.xml" +```xml hl_lines="5-7" + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + +``` + +### Testing with DynamoDB Local + +#### Unit tests + +To unit test your function with DynamoDB Local, you can refer to this guide to [setup with Maven](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html#apache-maven). + +=== "pom.xml" + + ```xml hl_lines="4-6 24-26 28-31 42 45-47" + + + + com.amazonaws + DynamoDBLocal + [1.12,2.0) + test + + + + io.github.ganadist.sqlite4java + libsqlite4java-osx-aarch64 + 1.0.392 + test + dylib + + + + + + + dynamodb-local-oregon + DynamoDB Local Release Repository + https://s3-us-west-2.amazonaws.com/dynamodb-local/release + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + ${project.build.directory}/native-libs + + + + idempotency + eu-central-1 + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + test-compile + + copy-dependencies + + + test + so,dll,dylib + ${project.build.directory}/native-libs + + + + + + ``` + +=== "AppTest.java" + + ```java hl_lines="13-18 24-30 34" + public class AppTest { + @Mock + private Context context; + private App app; + private static DynamoDbClient client; + + @BeforeAll + public static void setupDynamoLocal() { + int port = getFreePort(); + + // Initialize DynamoDBLocal + try { + DynamoDBProxyServer dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[]{ + "-inMemory", + "-port", + Integer.toString(port) + }); + dynamoProxy.start(); + } catch (Exception e) { + throw new RuntimeException(); + } + + // Initialize DynamoDBClient + 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(); + + // create the table (use same table name as in pom.xml) + 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() { + app.handleRequest(..., context); + // ... assert + } + } + ``` + +=== "App.java" + + ```java + public class App implements RequestHandler { + + public App(DynamoDbClient ddbClient) { + Idempotency.config().withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE_NAME")) + .withDynamoDbClient(ddbClient) + .build() + ).configure(); + } + + public App() { + this(null); + } + + @Idempotent + public SubscriptionResult handleRequest(final Subscription event, final Context context) { + // ... + } + ``` + + +#### SAM Local + +=== "App.java" + + ```java hl_lines="8 9 16" + public class App implements RequestHandler { + + public App() { + DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .httpClient(UrlConnectionHttpClient.builder().build()); + + if (System.getenv("AWS_SAM_LOCAL") != null) { + ddbBuilder.endpointOverride(URI.create("http://dynamo:8000")); + } else { + ddbBuilder.region(Region.of(System.getenv("AWS_REGION"))); + } + + Idempotency.config().withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE_NAME")) + .withDynamoDbClient(ddbBuilder.build()) + .build() + ).configure(); + } + + @Idempotent + public SubscriptionResult handleRequest(final Subscription event, final Context context) { + // ... + } + } + ``` + +=== "shell" + + ```shell hl_lines="2 6 7 12 16 21 22" + # use or create a docker network + docker network inspect sam-local || docker network create sam-local + + # start dynamodb-local with docker + docker run -d --rm -p 8000:8000 \ + --network sam-local \ + --name dynamo \ + amazon/dynamodb-local + + # create the idempotency table + aws dynamodb create-table + --table-name idempotency \ + --attribute-definitions AttributeName=id,AttributeType=S \ + --key-schema AttributeName=id,KeyType=HASH \ + --billing-mode PAY_PER_REQUEST \ + --endpoint-url http://localhost:8000 + + # invoke the function locally + sam local invoke IdempotentFunction \ + --event event.json \ + --env-vars env.json \ + --docker-network sam-local + ``` + +=== "env.json" + + ```json hl_lines="3" + { + "IdempotentFunction": { + "IDEMPOTENCY_TABLE_NAME": "idempotency" + } + } + ``` + +## Extra resources + +If you're interested in a deep dive on how Amazon uses idempotency when building our APIs, check out +[this article](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/). diff --git a/docs/utilities/serialization.md b/docs/utilities/serialization.md new file mode 100644 index 000000000..19ff00d37 --- /dev/null +++ b/docs/utilities/serialization.md @@ -0,0 +1,232 @@ +--- +title: Serialization Utilities +description: Utility +--- + +This module contains a set of utilities you may use in your Lambda functions, mainly associated with other modules like [validation](validation.md) and [idempotency](idempotency.md), to manipulate JSON. + +## JMESPath functions + +!!! Tip + [JMESPath](https://jmespath.org/){target="_blank"} is a query language for JSON used by AWS CLI and AWS Lambda Powertools for Java to get a specific part of a json. + +### Key features + +* Deserialize JSON from JSON strings, base64, and compressed data +* Use JMESPath to extract and combine data recursively + +### Getting started + +You might have events that contain encoded JSON payloads as string, base64, or even in compressed format. It is a common use case to decode and extract them partially or fully as part of your Lambda function invocation. + +You will generally use this in combination with other Lambda Powertools modules ([validation](validation.md) and [idempotency](idempotency.md)) where you might need to extract a portion of your data before using them. + +### Built-in functions + +Powertools provides the following JMESPath Functions to easily deserialize common encoded JSON payloads in Lambda functions: + +#### powertools_json function + +Use `powertools_json` function to decode any JSON string anywhere a JMESPath expression is allowed. + +Below example use this function to load the content from the body of an API Gateway request event as a JSON object and retrieve the id field in it: + +=== "MyHandler.java" + + ```java hl_lines="7" + public class MyHandler implements RequestHandler { + + public MyHandler() { + Idempotency.config() + .withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).id") + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + @Idempotent + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent event, final Context context) { + } + ``` + +=== "event" + + ```json hl_lines="2" + { + "body": "{\"message\": \"Lambda rocks\", \"id\": 43876123454654}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "queryStringParameters": { + "foo": "bar" + }, + "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", + }, + "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" + } + } + ``` + +#### powertools_base64 function + +Use `powertools_base64` function to decode any base64 data. + +Below sample will decode the base64 value within the `data` key, and decode the JSON string into a valid JSON before we can validate it. + +=== "MyEventHandler.java" + + ```java hl_lines="7" + import software.amazon.lambda.powertools.validation.ValidationUtils; + + public class MyEventHandler implements RequestHandler { + + @Override + public String handleRequest(MyEvent myEvent, Context context) { + validate(myEvent, "classpath:/schema.json", "powertools_base64(data)"); + return "OK"; + } + } + ``` +=== "schema.json" +```json +{ +"data" : "ewogICJpZCI6IDQzMjQyLAogICJuYW1lIjogIkZvb0JhciBYWSIsCiAgInByaWNlIjogMjU4Cn0=" +} +``` + +#### powertools_base64_gzip function + +Use `powertools_base64_gzip` function to decompress and decode base64 data. + +Below sample will decompress and decode base64 data. + +=== "MyEventHandler.java" + + ```java hl_lines="7" + import software.amazon.lambda.powertools.validation.ValidationUtils; + + public class MyEventHandler implements RequestHandler { + + @Override + public String handleRequest(MyEvent myEvent, Context context) { + validate(myEvent, "classpath:/schema.json", "powertools_base64_gzip(data)"); + return "OK"; + } + } + ``` + +=== "schema.json" + + ```json + { + "data" : "H4sIAAAAAAAA/6vmUlBQykxRslIwMTYyMdIBcfMSc1OBAkpu+flOiUUKEZFKYOGCosxkkLiRqQVXLQDnWo6bOAAAAA==" + } + ``` + + +### Bring your own JMESPath function + +!!! warning + This should only be used for advanced use cases where you have special formats not covered by the built-in functions. + Please open an issue in Github if you need us to add some common functions. + +Your function must extend `io.burt.jmespath.function.BaseFunction`, take a String as parameter and return a String. +You can read the [doc](https://github.com/burtcorp/jmespath-java#adding-custom-functions) for more information. + +Below is an example that takes some xml and transform it into json. Once your function is created, you need to add it +to powertools.You can then use it to do your validation or in idempotency module. + +=== "XMLFunction.java" + + ```java + public class XMLFunction extends BaseFunction { + public Base64Function() { + super("powertools_xml", ArgumentConstraints.typeOf(JmesPathType.STRING)); + } + + @Override + protected T callFunction(Adapter runtime, List> arguments) { + T value = arguments.get(0).value(); + String xmlString = runtime.toString(value); + + String jsonString = // ... transform xmlString to json + + return runtime.createString(jsonString); + } + } + ``` + +=== "Handler with validation API" + + ```java hl_lines="6 13" + ... + import software.amazon.lambda.powertools.validation.ValidationConfig; + import software.amazon.lambda.powertools.validation.ValidationUtils.validate; + + static { + JsonConfig.get().addFunction(new XMLFunction()); + } + + public class MyXMLEventHandler implements RequestHandler { + + @Override + public String handleRequest(MyEventWithXML myEvent, Context context) { + validate(myEvent, "classpath:/schema.json", "powertools_xml(path.to.xml_data)"); + return "OK"; + } + } + ``` + +=== "Handler with validation annotation" + + ```java hl_lines="6 12" + ... + import software.amazon.lambda.powertools.validation.ValidationConfig; + import software.amazon.lambda.powertools.validation.Validation; + + static { + JsonConfig.get().addFunction(new XMLFunction()); + } + + public class MyXMLEventHandler implements RequestHandler { + + @Override + @Validation(inboundSchema="classpath:/schema.json", envelope="powertools_xml(path.to.xml_data)") + public String handleRequest(MyEventWithXML myEvent, Context context) { + return "OK"; + } + } + ``` diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 862e668c0..28d8c7a20 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -221,145 +221,6 @@ This is quite powerful because you can use JMESPath Query language to extract re to [pipe expressions](https://jmespath.org/tutorial.html#pipe-expressions) and [function](https://jmespath.org/tutorial.html#functions) expressions, where you'd extract what you need before validating the actual payload. -## JMESPath functions - -JMESPath functions ensure to make an operation on a specific part of the json.validate - -Powertools provides two built-in functions: - -### powertools_base64 function - -Use `powertools_base64` function to decode any base64 data. - -Below sample will decode the base64 value within the data key, and decode the JSON string into a valid JSON before we can validate it. - -=== "MyEventHandler.java" - - ```java hl_lines="7" - import software.amazon.lambda.powertools.validation.ValidationUtils; - - public class MyEventHandler implements RequestHandler { - - @Override - public String handleRequest(MyEvent myEvent, Context context) { - validate(myEvent, "classpath:/schema.json", "powertools_base64(data)"); - return "OK"; - } - } - ``` -=== "schema.json" - ```json - { - "data" : "ewogICJpZCI6IDQzMjQyLAogICJuYW1lIjogIkZvb0JhciBYWSIsCiAgInByaWNlIjogMjU4Cn0=" - } - ``` - -### powertools_base64_gzip function - -Use `powertools_base64_gzip` function to decompress and decode base64 data. - -Below sample will decompress and decode base64 data. - -=== "MyEventHandler.java" - - ```java hl_lines="7" - import software.amazon.lambda.powertools.validation.ValidationUtils; - - public class MyEventHandler implements RequestHandler { - - @Override - public String handleRequest(MyEvent myEvent, Context context) { - validate(myEvent, "classpath:/schema.json", "powertools_base64_gzip(data)"); - return "OK"; - } - } - ``` - -=== "schema.json" - - ```json - { - "data" : "H4sIAAAAAAAA/6vmUlBQykxRslIwMTYyMdIBcfMSc1OBAkpu+flOiUUKEZFKYOGCosxkkLiRqQVXLQDnWo6bOAAAAA==" - } - ``` - -!!! note - You don't need any function to transform a JSON String into a JSON object, powertools-validation will do it for you. - In the 2 previous example, data contains JSON. Just provide the function to transform the base64 / gzipped / ... string into a clear JSON string. - -### Bring your own JMESPath function - -!!! warning - This should only be used for advanced use cases where you have special formats not covered by the built-in functions. - New functions will be added to the 2 built-in ones. - -Your function must extend `io.burt.jmespath.function.BaseFunction`, take a String as parameter and return a String. -You can read the [doc](https://github.com/burtcorp/jmespath-java#adding-custom-functions) for more information. - -Below is an example that takes some xml and transform it into json. Once your function is created, you need to add it -to powertools.You can then use it to do your validation or using annotation. - -=== "XMLFunction.java" - - ```java - public class XMLFunction extends BaseFunction { - public Base64Function() { - super("powertools_xml", ArgumentConstraints.typeOf(JmesPathType.STRING)); - } - - @Override - protected T callFunction(Adapter runtime, List> arguments) { - T value = arguments.get(0).value(); - String xmlString = runtime.toString(value); - - String jsonString = // ... transform xmlString to json - - return runtime.createString(jsonString); - } - } - ``` - -=== "Handler with validation API" - - ```java hl_lines="6 13" - ... - import software.amazon.lambda.powertools.validation.ValidationConfig; - import software.amazon.lambda.powertools.validation.ValidationUtils.validate; - - static { - ValidationConfig.get().addFunction(new XMLFunction()); - } - - public class MyXMLEventHandler implements RequestHandler { - - @Override - public String handleRequest(MyEventWithXML myEvent, Context context) { - validate(myEvent, "classpath:/schema.json", "powertools_xml(path.to.xml_data)"); - return "OK"; - } - } - ``` - -=== "Handler with validation annotation" - - ```java hl_lines="6 12" - ... - import software.amazon.lambda.powertools.validation.ValidationConfig; - import software.amazon.lambda.powertools.validation.Validation; - - static { - ValidationConfig.get().addFunction(new XMLFunction()); - } - - public class MyXMLEventHandler implements RequestHandler { - - @Override - @Validation(inboundSchema="classpath:/schema.json", envelope="powertools_xml(path.to.xml_data)") - public String handleRequest(MyEventWithXML myEvent, Context context) { - return "OK"; - } - } - ``` ## Change the schema version By default, powertools-validation is configured with [V7](https://json-schema.org/draft-07/json-schema-release-notes.html). diff --git a/example/HelloWorldFunction/build.gradle b/example/HelloWorldFunction/build.gradle index d7b0221d6..b342a4931 100644 --- a/example/HelloWorldFunction/build.gradle +++ b/example/HelloWorldFunction/build.gradle @@ -4,6 +4,7 @@ plugins{ } repositories { + mavenLocal() mavenCentral() } @@ -15,8 +16,11 @@ dependencies { aspect 'software.amazon.lambda:powertools-parameters:1.10.3' aspect 'software.amazon.lambda:powertools-validation:1.10.3' + implementation 'software.amazon.lambda:powertools-idempotency:1.10.3' + aspectpath 'software.amazon.lambda:powertools-idempotency:1.10.3' + implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' - implementation 'com.amazonaws:aws-lambda-java-events:3.1.0' + implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' implementation 'org.apache.logging.log4j:log4j-api:2.16.0' implementation 'org.apache.logging.log4j:log4j-core:2.16.0' diff --git a/example/HelloWorldFunction/pom.xml b/example/HelloWorldFunction/pom.xml index e742c66cf..96786e152 100644 --- a/example/HelloWorldFunction/pom.xml +++ b/example/HelloWorldFunction/pom.xml @@ -43,6 +43,11 @@ powertools-sqs 1.10.3 + + software.amazon.lambda + powertools-idempotency + 1.10.3 + com.amazonaws aws-lambda-java-core @@ -51,7 +56,7 @@ com.amazonaws aws-lambda-java-events - 3.1.0 + 3.11.0 org.apache.logging.log4j diff --git a/example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java b/example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java new file mode 100644 index 000000000..64b5d79a5 --- /dev/null +++ b/example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java @@ -0,0 +1,84 @@ +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 org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +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 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; + +public class AppIdempotency implements RequestHandler { + private final static Logger LOG = LogManager.getLogger(); + + public AppIdempotency() { + // we need to initialize idempotency configuration before the handleRequest method is called + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).address") + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName("idempotency_table") + .build() + ).configure(); + } + + + /** + * Try with: + *
+     *     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"}'
+     * 
+ * @param input + * @param context + * @return + */ + @Idempotent + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map 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 { + 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); + } + } + + // 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/example/template.yaml b/example/template.yaml index e3dddad4a..901d8306d 100644 --- a/example/template.yaml +++ b/example/template.yaml @@ -53,6 +53,35 @@ Resources: Path: /helloparams Method: get + IdempotencyTable: + Type: AWS::Serverless::SimpleTable + Properties: + TableName: idempotency_table + PrimaryKey: + Name: id + Type: String + + HelloWorldIdempotentFunction: + 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: HelloWorldFunction + Handler: helloworld.AppIdempotency::handleRequest + 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_LOG_LEVEL: INFO + AWS_ENDPOINT_DISCOVERY_ENABLED: false + Tracing: Active + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref IdempotencyTable + 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: /helloidem + Method: post + UserPwd: Type: AWS::SecretsManager::Secret Properties: @@ -171,3 +200,9 @@ Outputs: Description: "Hello World Params Lambda Function ARN" Value: !GetAtt HelloWorldParamsFunction.Arn + HelloWorldIdempotencyApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World idempotency function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/helloidem/" + HelloWorldIdempotencyFunction: + Description: "Hello World Idempotency Lambda Function ARN" + Value: !GetAtt HelloWorldIdempotentFunction.Arn diff --git a/mkdocs.yml b/mkdocs.yml index 6ed6ec179..0881cdc5c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,11 +10,13 @@ nav: - core/tracing.md - core/metrics.md - Utilities: + - utilities/idempotency.md - utilities/parameters.md - utilities/sqs_large_message_handling.md - utilities/batch.md - utilities/validation.md - utilities/custom_resources.md + - utilities/serialization.md theme: name: material diff --git a/pom.xml b/pom.xml index 5af81b05b..ecc2b305f 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,7 @@ powertools-core + powertools-serialization powertools-logging powertools-tracing powertools-sqs @@ -36,6 +37,7 @@ powertools-validation powertools-test-suite powertools-cloudformation + powertools-idempotency @@ -90,6 +92,11 @@ powertools-core ${project.version}
+ + software.amazon.lambda + powertools-serialization + ${project.version} + software.amazon.lambda powertools-logging diff --git a/powertools-idempotency/README.md b/powertools-idempotency/README.md new file mode 100644 index 000000000..99b9c7ac8 --- /dev/null +++ b/powertools-idempotency/README.md @@ -0,0 +1,14 @@ +## Idempotency +Refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/idempotency/) for details on how to use this module in your Lambda function. + +### Contributing +This module provides a persistence layer with a built-in store using DynamoDB. +To unit test it, we use [DynamoDB Local](https://docs.aws.amazon.com/fr_fr/amazondynamodb/latest/developerguide/DynamoDBLocal.html) which depends on sqlite. +You may encounter the following issue on Apple M1 chips: +``` +com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: java.lang.UnsatisfiedLinkError: native-libs/libsqlite4java-osx-1.0.392.dylib: dlopen(native-libs/libsqlite4java-osx-1.0.392.dylib, 1): no suitable image found. Did find: +native-libs/libsqlite4java-osx-1.0.392.dylib: no matching architecture in universal wrapper +``` + +In such case, try with another JDK. See [stackoverflow](https://stackoverflow.com/questions/66635424/dynamodb-local-setup-on-m1-apple-silicon-mac) and this [issue](https://github.com/aws-samples/aws-dynamodb-examples/issues/22) for more info. +We'll update the dependencies as soon as it will be solved. \ No newline at end of file diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml new file mode 100644 index 000000000..785447d56 --- /dev/null +++ b/powertools-idempotency/pom.xml @@ -0,0 +1,214 @@ + + + 4.0.0 + + + software.amazon.lambda + powertools-parent + 1.10.3 + + + powertools-idempotency + jar + + AWS Lambda Powertools Java library Idempotency + + + + https://aws.amazon.com/lambda/ + + GitHub Issues + https://github.com/awslabs/aws-lambda-powertools-java/issues + + + https://github.com/awslabs/aws-lambda-powertools-java.git + + + + AWS Lambda Powertools team + Amazon Web Services + https://aws.amazon.com/ + + + + + + ossrh + https://aws.oss.sonatype.org/content/repositories/snapshots + + + + + + software.amazon.lambda + powertools-core + + + software.amazon.lambda + powertools-serialization + + + com.amazonaws + aws-lambda-java-core + + + software.amazon.awssdk + dynamodb + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + software.amazon.awssdk + url-connection-client + ${aws.sdk.version} + + + org.aspectj + aspectjrt + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit-pioneer + junit-pioneer + 1.5.0 + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + + + org.apache.commons + commons-lang3 + test + + + org.assertj + assertj-core + test + + + com.amazonaws + aws-lambda-java-events + test + + + com.amazonaws + aws-lambda-java-tests + + + com.amazonaws + DynamoDBLocal + [1.12,2.0) + test + + + + io.github.ganadist.sqlite4java + libsqlite4java-osx-aarch64 + 1.0.392 + test + dylib + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.build.directory}/native-libs + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + test-compile + + copy-dependencies + + + test + so,dll,dylib + ${project.build.directory}/native-libs + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + software.amazon.awssdk.enhanced.dynamodb + + + + + + + + + jdk16 + + [16,) + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + + + + + + dynamodb-local-oregon + DynamoDB Local Release Repository + https://s3.eu-central-1.amazonaws.com/dynamodb-local-frankfurt/release + + + \ No newline at end of file diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java new file mode 100644 index 000000000..d8f7a9a13 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java @@ -0,0 +1,20 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +public class Constants { + public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME"; + public static final String AWS_REGION_ENV = "AWS_REGION"; + public static final String IDEMPOTENCY_DISABLED_ENV = "POWERTOOLS_IDEMPOTENCY_DISABLED"; +} 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 new file mode 100644 index 000000000..1ff2ed47f --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java @@ -0,0 +1,108 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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: + *
    + *
  • The persistence layer to use for persisting the request and response of the function (mandatory).
  • + *
  • The general configuration for idempotency (optional, see {@link IdempotencyConfig.Builder} methods to see defaults values.
  • + *
+ *
+ * Use it before the function handler ({@link com.amazonaws.services.lambda.runtime.RequestHandler#handleRequest(Object, Context)}) + * get called. + *
+ * Example: + *
+ *     Idempotency.config().withPersistenceStore(...).configure();
+ * 
+ */ +public class Idempotency { + private IdempotencyConfig config; + private BasePersistenceStore persistenceStore; + + private Idempotency() { + } + + public IdempotencyConfig getConfig() { + return config; + } + + public BasePersistenceStore getPersistenceStore() { + if (persistenceStore == null) { + throw new IllegalStateException("Persistence Store is null, did you call 'configure()'?"); + } + return persistenceStore; + } + + private void setConfig(IdempotencyConfig config) { + this.config = config; + } + + private void setPersistenceStore(BasePersistenceStore persistenceStore) { + this.persistenceStore = persistenceStore; + } + + private static class Holder { + private final static Idempotency instance = new Idempotency(); + } + + public static Idempotency getInstance() { + return Holder.instance; + } + + /** + * 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 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/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java new file mode 100644 index 000000000..4089d3ed8 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java @@ -0,0 +1,217 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; + +import java.time.Duration; + +/** + * Configuration of the idempotency feature. Use the {@link Builder} to create an instance. + */ +public class IdempotencyConfig { + private final int localCacheMaxItems; + private final boolean useLocalCache; + private final long expirationInSeconds; + private final String eventKeyJMESPath; + private final String payloadValidationJMESPath; + private final boolean throwOnNoIdempotencyKey; + private final String hashFunction; + + private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, long expirationInSeconds, String hashFunction) { + this.localCacheMaxItems = localCacheMaxItems; + this.useLocalCache = useLocalCache; + this.expirationInSeconds = expirationInSeconds; + this.eventKeyJMESPath = eventKeyJMESPath; + this.payloadValidationJMESPath = payloadValidationJMESPath; + this.throwOnNoIdempotencyKey = throwOnNoIdempotencyKey; + this.hashFunction = hashFunction; + } + + public int getLocalCacheMaxItems() { + return localCacheMaxItems; + } + + public boolean useLocalCache() { + return useLocalCache; + } + + public long getExpirationInSeconds() { + return expirationInSeconds; + } + + public String getEventKeyJMESPath() { + return eventKeyJMESPath; + } + + public String getPayloadValidationJMESPath() { + return payloadValidationJMESPath; + } + + public boolean throwOnNoIdempotencyKey() { + return throwOnNoIdempotencyKey; + } + + public String getHashFunction() { + return hashFunction; + } + + + /** + * Create a builder that can be used to configure and create a {@link IdempotencyConfig}. + * + * @return a new instance of {@link IdempotencyConfig.Builder} + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private int localCacheMaxItems = 256; + private boolean useLocalCache = false; + private long expirationInSeconds = 60 * 60; // 1 hour + private String eventKeyJMESPath; + private String payloadValidationJMESPath; + private boolean throwOnNoIdempotencyKey = false; + private String hashFunction = "MD5"; + + /** + * Initialize and return an instance of {@link IdempotencyConfig}.
+ * Example:
+ *
+         * IdempotencyConfig.builder().withUseLocalCache().build();
+         * 
+ * This instance must then be passed to the {@link Idempotency.Config}: + *
+         * Idempotency.config().withConfig(config).configure();
+         * 
+ * @return an instance of {@link IdempotencyConfig}. + */ + public IdempotencyConfig build() { + return new IdempotencyConfig( + eventKeyJMESPath, + payloadValidationJMESPath, + throwOnNoIdempotencyKey, + useLocalCache, + localCacheMaxItems, + expirationInSeconds, + hashFunction); + } + + /** + * A JMESPath expression to extract the idempotency key from the event record.
+ * See https://jmespath.org/ for more details.
+ * Common paths are:
    + *
  • powertools_json(body) for APIGatewayProxyRequestEvent and APIGatewayV2HTTPEvent
  • + *
  • Records[*].powertools_json(body) for SQSEvent
  • + *
  • Records[0].Sns.Message | powertools_json(@) for SNSEvent
  • + *
  • detail for ScheduledEvent (EventBridge / CloudWatch events)
  • + *
  • Records[*].kinesis.powertools_json(powertools_base64(data)) for KinesisEvent
  • + *
  • Records[*].powertools_json(powertools_base64(data)) for KinesisFirehoseEvent
  • + *
  • ...
  • + *
+ * + * + * @param eventKeyJMESPath path of the key in the Lambda event + * @return the instance of the builder (to chain operations) + */ + public Builder withEventKeyJMESPath(String eventKeyJMESPath) { + this.eventKeyJMESPath = eventKeyJMESPath; + return this; + } + + /** + * 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 + * @return the instance of the builder (to chain operations) + */ + public Builder withLocalCacheMaxItems(int localCacheMaxItems) { + this.localCacheMaxItems = localCacheMaxItems; + return this; + } + + /** + * 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} + * @return the instance of the builder (to chain operations) + */ + public Builder withUseLocalCache(boolean useLocalCache) { + this.useLocalCache = useLocalCache; + return this; + } + + /** + * The number of seconds to wait before a record is expired + * + * @param expiration expiration of the record in the store + * @return the instance of the builder (to chain operations) + */ + public Builder withExpiration(Duration expiration) { + this.expirationInSeconds = expiration.getSeconds(); + return this; + } + + /** + * A JMESPath expression to extract the payload to be validated from the event record.
+ * See https://jmespath.org/ for more details. + * + * @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) { + this.payloadValidationJMESPath = payloadValidationJMESPath; + return this; + } + + /** + * 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. + * @return the instance of the builder (to chain operations) + */ + public Builder withThrowOnNoIdempotencyKey(boolean throwOnNoIdempotencyKey) { + this.throwOnNoIdempotencyKey = throwOnNoIdempotencyKey; + return this; + } + + /** + * Throw an exception if no idempotency key was found in the request. + * Shortcut for {@link #withThrowOnNoIdempotencyKey(boolean)}, forced as true + * + * @return the instance of the builder (to chain operations) + */ + public Builder withThrowOnNoIdempotencyKey() { + return withThrowOnNoIdempotencyKey(true); + } + + /** + * Function to use for calculating hashes, by default MD5. + * + * @param hashFunction Can be any algorithm supported by {@link java.security.MessageDigest}, most commons are
    + *
  • MD5
  • + *
  • SHA-1
  • + *
  • SHA-256
+ * @return the instance of the builder (to chain operations) + */ + public Builder withHashFunction(String hashFunction) { + this.hashFunction = hashFunction; + return this; + } + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java new file mode 100644 index 000000000..92a0a3d49 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @IdempotencyKey is used to signal that a method parameter is used as a key for idempotency.
+ * Must be used in conjunction with the @Idempotency annotation.
+ * Example:
+ *
+ *     @Idempotent
+ *     private MyObject subMethod(String param1, @IdempotencyKey String param2) {
+ *         // ...
+ *         return something;
+ *     }
+ * 
+ * Note: This annotation is not needed when the method only has one parameter. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface IdempotencyKey { +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java new file mode 100644 index 000000000..e7cace1fb --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @Idempotent is used to signal that the annotated method is idempotent:
+ * Calling this method one or multiple times with the same parameter will always return the same result.
+ * This annotation can be placed on the + * {@link com.amazonaws.services.lambda.runtime.RequestHandler#handleRequest(Object, Context)} + * method of a Lambda function:
+ *
+ *     @Idempotent
+ *     public String handleRequest(String event, Context ctx) {
+ *         // ...
+ *         return something;
+ *     }
+ * 
+ *
+ * It can also be placed on another method. In that case you may need to use the @{@link IdempotencyKey} + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Idempotent { + +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java new file mode 100644 index 000000000..3d5ee93c5 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * This exception is thrown when the same payload is sent + * while the previous one was not yet fully stored in the persistence layer (marked as COMPLETED). + */ +public class IdempotencyAlreadyInProgressException extends RuntimeException { + private static final long serialVersionUID = 7229475093418832265L; + + public IdempotencyAlreadyInProgressException(String msg) { + super(msg); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java new file mode 100644 index 000000000..0d3844641 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * Exception thrown when Idempotency is not well configured: + *
    + *
  • An annotated method does not return anything
  • + *
  • An annotated method does not have parameters or more than one without + * the {@link software.amazon.lambda.powertools.idempotency.IdempotencyKey} annotation
  • + *
+ */ +public class IdempotencyConfigurationException extends RuntimeException { + private static final long serialVersionUID = 560587720373305487L; + + public IdempotencyConfigurationException(String message) { + super(message); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java new file mode 100644 index 000000000..c6fe38d23 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * IdempotencyInconsistentStateException can happen under rare but expected cases + * when persistent state changes in the small-time between put & get requests. + */ +public class IdempotencyInconsistentStateException extends RuntimeException { + private static final long serialVersionUID = -4293951999802300672L; + + public IdempotencyInconsistentStateException(String msg, Exception e) { + super(msg, e); + } + + public IdempotencyInconsistentStateException(String msg) { + super(msg); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java new file mode 100644 index 000000000..088db59c0 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * Exception thrown when trying to store an item which already exists. + */ +public class IdempotencyItemAlreadyExistsException extends RuntimeException { + private static final long serialVersionUID = 9027152772149436500L; + + public IdempotencyItemAlreadyExistsException() { + super(); + } + + public IdempotencyItemAlreadyExistsException(String msg, Throwable e) { + super(msg, e); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java new file mode 100644 index 000000000..afae2554e --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * Exception thrown when the item was not found in the persistence store. + */ +public class IdempotencyItemNotFoundException extends RuntimeException{ + private static final long serialVersionUID = 4818288566747993032L; + + public IdempotencyItemNotFoundException(String idempotencyKey) { + super("Item with idempotency key "+ idempotencyKey + " not found"); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java new file mode 100644 index 000000000..7259dff0f --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * Exception thrown only when using {@link software.amazon.lambda.powertools.idempotency.IdempotencyConfig#throwOnNoIdempotencyKey()}, + * and if a key could not be found in the event (for example when having a bad JMESPath configured) + */ +public class IdempotencyKeyException extends RuntimeException { + private static final long serialVersionUID = -8514965705001281773L; + + public IdempotencyKeyException(String message) { + super(message); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java new file mode 100644 index 000000000..fa49b746c --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +/** + * Exception thrown when a technical error occurred with the persistence layer (eg. insertion, deletion, ... in database) + */ +public class IdempotencyPersistenceLayerException extends RuntimeException { + private static final long serialVersionUID = 6781832947434168547L; + + public IdempotencyPersistenceLayerException(String msg, Exception e) { + super(msg, e); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java new file mode 100644 index 000000000..5aee228eb --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.exceptions; + +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; + +/** + * Exception thrown only when using {@link IdempotencyConfig#getPayloadValidationJMESPath()} is configured + * and the payload changed between two calls (but with the same idempotency key). + */ +public class IdempotencyValidationException extends RuntimeException { + private static final long serialVersionUID = -4218652810664634761L; + + public IdempotencyValidationException() { + super(); + } + + public IdempotencyValidationException(String message) { + super(message); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java new file mode 100644 index 000000000..1f3724919 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -0,0 +1,161 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import com.fasterxml.jackson.databind.JsonNode; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.exceptions.*; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +import java.time.Instant; + +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; + +/** + * 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 JsonNode data; + private final BasePersistenceStore persistenceStore; + + public IdempotencyHandler(ProceedingJoinPoint pjp, String functionName, JsonNode payload) { + this.pjp = pjp; + this.data = payload; + persistenceStore = Idempotency.getInstance().getPersistenceStore(); + persistenceStore.configure(Idempotency.getInstance().getConfig(), functionName); + } + + /** + * Main entry point for handling idempotent execution of a function. + * + * @return function response + */ + public Object handle() throws Throwable { + // IdempotencyInconsistentStateException can happen under rare but expected cases + // when persistent state changes in the small time between put & get requests. + // In most cases we can retry successfully on this exception. + for (int i = 0; true; i++) { + try { + return processIdempotency(); + } catch (IdempotencyInconsistentStateException e) { + if (i == MAX_RETRIES) { + throw e; + } + } + } + } + + /** + * Process the function with idempotency + * + * @return function response + */ + private Object processIdempotency() throws Throwable { + try { + // We call saveInProgress first as an optimization for the most common case where no idempotent record + // already exists. If it succeeds, there's no need to call getRecord. + persistenceStore.saveInProgress(data, Instant.now()); + } catch (IdempotencyItemAlreadyExistsException iaee) { + DataRecord record = getIdempotencyRecord(); + return handleForStatus(record); + } catch (IdempotencyKeyException ike) { + throw ike; + } catch (Exception e) { + throw new IdempotencyPersistenceLayerException("Failed to save in progress record to idempotency store. If you believe this is a powertools bug, please open an issue.", e); + } + return getFunctionResponse(); + } + + /** + * Retrieve the idempotency record from the persistence layer. + * + * @return the record if available + */ + private DataRecord getIdempotencyRecord() { + try { + return persistenceStore.getRecord(data, Instant.now()); + } catch (IdempotencyItemNotFoundException e) { + // This code path will only be triggered if the record is removed between saveInProgress and getRecord + LOG.debug("An existing idempotency record was deleted before we could fetch it"); + throw new IdempotencyInconsistentStateException("saveInProgress and getRecord return inconsistent results", e); + } catch (IdempotencyValidationException | IdempotencyKeyException vke) { + throw vke; + } catch (Exception e) { + throw new IdempotencyPersistenceLayerException("Failed to get record from idempotency store. If you believe this is a powertools bug, please open an issue.", e); + } + } + + /** + * Take appropriate action based on data_record's status + * + * @param record DataRecord + * @return Function's response previously used for this idempotency key, if it has successfully executed already. + */ + private Object handleForStatus(DataRecord record) { + // This code path will only be triggered if the record becomes expired between the saveInProgress call and here + if (EXPIRED.equals(record.getStatus())) { + throw new IdempotencyInconsistentStateException("saveInProgress and getRecord return inconsistent results"); + } + + if (INPROGRESS.equals(record.getStatus())) { + throw new IdempotencyAlreadyInProgressException("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()); + return JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), returnType); + } catch (Exception e) { + throw new IdempotencyPersistenceLayerException("Unable to get function response as " + returnType.getSimpleName(), e); + } + } + + private Object getFunctionResponse() throws Throwable { + Object response; + try { + response = pjp.proceed(pjp.getArgs()); + } catch (Throwable handlerException) { + // We need these nested blocks to preserve function's exception in case the persistence store operation + // also raises an exception + try { + persistenceStore.deleteRecord(data, handlerException); + } catch (IdempotencyKeyException ke) { + throw ke; + } catch (Exception e) { + throw new IdempotencyPersistenceLayerException("Failed to delete record from idempotency store. If you believe this is a powertools bug, please open an issue.", e); + } + throw handlerException; + } + + try { + persistenceStore.saveSuccess(data, response, Instant.now()); + } catch (Exception e) { + throw new IdempotencyPersistenceLayerException("Failed to update record state to success in idempotency store. If you believe this is a powertools bug, please open an issue.", e); + } + return response; + } + +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java new file mode 100644 index 000000000..b372a34a4 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -0,0 +1,94 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import com.fasterxml.jackson.databind.JsonNode; +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.MethodSignature; +import software.amazon.lambda.powertools.idempotency.Constants; +import software.amazon.lambda.powertools.idempotency.IdempotencyKey; +import software.amazon.lambda.powertools.idempotency.Idempotent; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; + +/** + * Aspect that handles the {@link Idempotent} annotation. + * It uses the {@link IdempotencyHandler} to actually do the job. + */ +@Aspect +public class IdempotentAspect { + @SuppressWarnings({"EmptyMethod"}) + @Pointcut("@annotation(idempotent)") + public void callAt(Idempotent idempotent) { + } + + @Around(value = "callAt(idempotent) && execution(@Idempotent * *.*(..))", argNames = "pjp,idempotent") + public Object around(ProceedingJoinPoint pjp, + Idempotent idempotent) throws Throwable { + + String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); + if (idempotencyDisabledEnv != null && !idempotencyDisabledEnv.equals("false")) { + return pjp.proceed(pjp.getArgs()); + } + + Method method = ((MethodSignature) pjp.getSignature()).getMethod(); + if (method.getReturnType().equals(void.class)) { + throw new IdempotencyConfigurationException("The annotated method doesn't return anything. Unable to perform idempotency on void return type"); + } + + JsonNode payload = getPayload(pjp, method); + if (payload == null) { + throw new IdempotencyConfigurationException("Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); + } + + IdempotencyHandler idempotencyHandler = new IdempotencyHandler(pjp, method.getName(), payload); + return idempotencyHandler.handle(); + } + + /** + * Retrieve the payload from the annotated method parameters + * @param pjp joinPoint + * @param method the annotated method + * @return the payload used for idempotency + */ + private JsonNode getPayload(ProceedingJoinPoint pjp, Method method) { + JsonNode payload = null; + // handleRequest or method with one parameter: get the first one + if ((isHandlerMethod(pjp) && placedOnRequestHandler(pjp)) + || pjp.getArgs().length == 1) { + payload = JsonConfig.get().getObjectMapper().valueToTree(pjp.getArgs()[0]); + } else { + // Look for a parameter annotated with @IdempotencyKey + Annotation[][] annotations = method.getParameterAnnotations(); + for (int i = 0; i < annotations.length && payload == null; i++) { + Annotation[] annotationsRow = annotations[i]; + for (int j = 0; j < annotationsRow.length && payload == null; j++) { + if (annotationsRow[j].annotationType().equals(IdempotencyKey.class)) { + payload = JsonConfig.get().getObjectMapper().valueToTree(pjp.getArgs()[i]); + } + } + } + } + return payload; + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java new file mode 100644 index 000000000..a017c211a --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.cache; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Implementation of a simple LRU Cache based on a {@link LinkedHashMap} + * See here. + * @param Type of the keys + * @param Types of the values + */ +public class LRUCache extends LinkedHashMap { + + private static final long serialVersionUID = 3108262622672699228L; + private final int capacity; + + public LRUCache(int capacity) { + super(capacity * 4 / 3, 0.75f, true); + this.capacity = capacity; + } + + @Override + protected boolean removeEldestEntry(Map.Entry entry) { + return (size() > this.capacity); + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java new file mode 100644 index 000000000..a65b4c193 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -0,0 +1,350 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectWriter; +import io.burt.jmespath.Expression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.lambda.powertools.idempotency.Constants; +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.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +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.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * Persistence layer that will store the idempotency result. + * Base implementation. See {@link DynamoDBPersistenceStore} for an implementation (default one) + * Extends this class to use your own implementation (DocumentDB, Elasticache, ...) + */ +public abstract class BasePersistenceStore implements PersistenceStore { + + private static final Logger LOG = LoggerFactory.getLogger(BasePersistenceStore.class); + + private String functionName = ""; + private boolean configured = false; + private long expirationInSeconds = 60 * 60; // 1 hour default + private boolean useLocalCache = false; + private LRUCache cache; + private String eventKeyJMESPath; + private Expression eventKeyCompiledJMESPath; + protected boolean payloadValidationEnabled = false; + private Expression validationKeyJMESPath; + private boolean throwOnNoIdempotencyKey = false; + private MessageDigest hashAlgorithm; + + /** + * Initialize the base persistence layer from the configuration settings + * + * @param config Idempotency configuration settings + * @param functionName The name of the function being decorated + */ + public void configure(IdempotencyConfig config, String functionName) { + String funcEnv = System.getenv(Constants.LAMBDA_FUNCTION_NAME_ENV); + this.functionName = funcEnv != null ? funcEnv : "testFunction"; + if (!StringUtils.isEmpty(functionName)) { + this.functionName += "." + functionName; + } + + if (configured) { + // prevent being reconfigured multiple times + return; + } + + eventKeyJMESPath = config.getEventKeyJMESPath(); + if (eventKeyJMESPath != null) { + eventKeyCompiledJMESPath = JsonConfig.get().getJmesPath().compile(eventKeyJMESPath); + } + if (config.getPayloadValidationJMESPath() != null) { + validationKeyJMESPath = JsonConfig.get().getJmesPath().compile(config.getPayloadValidationJMESPath()); + payloadValidationEnabled = true; + } + throwOnNoIdempotencyKey = config.throwOnNoIdempotencyKey(); + + useLocalCache = config.useLocalCache(); + if (useLocalCache) { + cache = new LRUCache<>(config.getLocalCacheMaxItems()); + } + expirationInSeconds = config.getExpirationInSeconds(); + + try { + hashAlgorithm = MessageDigest.getInstance(config.getHashFunction()); + } catch (NoSuchAlgorithmException e) { + LOG.warn("Error instantiating {} hash function, trying with MD5", config.getHashFunction()); + try { + hashAlgorithm = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException("Unable to instantiate MD5 digest", ex); + } + } + configured = true; + } + + /** + * Save record of function's execution completing successfully + * + * @param data Payload + * @param result the response from the function + */ + public void saveSuccess(JsonNode data, Object result, Instant now) { + ObjectWriter writer = JsonConfig.get().getObjectMapper().writer(); + try { + String responseJson = writer.writeValueAsString(result); + DataRecord record = new DataRecord( + getHashedIdempotencyKey(data), + DataRecord.Status.COMPLETED, + getExpiryEpochSecond(now), + responseJson, + getHashedPayload(data) + ); + LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", record.getIdempotencyKey()); + updateRecord(record); + saveToCache(record); + } catch (JsonProcessingException e) { + // TODO : throw ? + throw new RuntimeException("Error while serializing the response", e); + } + } + + /** + * Save record of function's execution being in progress + * + * @param data Payload + * @param now + */ + public void saveInProgress(JsonNode data, Instant now) throws IdempotencyItemAlreadyExistsException { + String idempotencyKey = getHashedIdempotencyKey(data); + + if (retrieveFromCache(idempotencyKey, now) != null) { + throw new IdempotencyItemAlreadyExistsException(); + } + + DataRecord record = new DataRecord( + idempotencyKey, + DataRecord.Status.INPROGRESS, + getExpiryEpochSecond(now), + null, + getHashedPayload(data) + ); + LOG.debug("saving in progress record for idempotency key: {}", record.getIdempotencyKey()); + putRecord(record, now); + } + + /** + * Delete record from the persistence store + * + * @param data Payload + * @param throwable The throwable thrown by the function + */ + public void deleteRecord(JsonNode data, Throwable throwable) { + String idemPotencyKey = getHashedIdempotencyKey(data); + + LOG.debug("Function raised an exception {}. " + + "Clearing in progress record in persistence store for idempotency key: {}", + throwable.getClass(), + idemPotencyKey); + + deleteRecord(idemPotencyKey); + deleteFromCache(idemPotencyKey); + } + + /** + * Retrieve idempotency key for data provided, fetch from persistence store, and convert to DataRecord. + * + * @param data Payload + * @return DataRecord representation of existing record found in persistence store + * @throws IdempotencyValidationException Payload doesn't match the stored record for the given idempotency key + * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key + */ + public DataRecord getRecord(JsonNode data, Instant now) throws IdempotencyValidationException, IdempotencyItemNotFoundException { + String idemPotencyKey = getHashedIdempotencyKey(data); + + DataRecord cachedRecord = retrieveFromCache(idemPotencyKey, now); + if (cachedRecord != null) { + LOG.debug("Idempotency record found in cache with idempotency key: {}", idemPotencyKey); + validatePayload(data, cachedRecord); + return cachedRecord; + } + + DataRecord record = getRecord(idemPotencyKey); + saveToCache(record); + validatePayload(data, record); + return record; + } + + /** + * Extract idempotency key and return a hashed representation + * + * @param data incoming data + * @return Hashed representation of the data extracted by the jmespath expression + */ + private String getHashedIdempotencyKey(JsonNode data) { + JsonNode node = data; + + if (eventKeyJMESPath != null) { + node = eventKeyCompiledJMESPath.search(data); + } + + if (isMissingIdemPotencyKey(node)) { + if (throwOnNoIdempotencyKey) { + throw new IdempotencyKeyException("No data found to create a hashed idempotency key"); + } + LOG.warn("No data found to create a hashed idempotency key. JMESPath: {}", eventKeyJMESPath); + } + + String hash = generateHash(node); + return functionName + "#" + hash; + } + + private boolean isMissingIdemPotencyKey(JsonNode data) { + if (data.isContainerNode()) { + Stream> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.fields(), Spliterator.ORDERED), false); + return stream.allMatch(e -> e.getValue().isNull()); + } + return data.isNull(); + } + + /** + * Extract payload using validation key jmespath and return a hashed representation + * + * @param data Payload + * @return Hashed representation of the data extracted by the jmespath expression + */ + private String getHashedPayload(JsonNode data) { + if (!payloadValidationEnabled) { + return ""; + } + JsonNode object = validationKeyJMESPath.search(data); + return generateHash(object); + } + + /** + * Generate a hash value from the provided data + * + * @param data data to hash + * @return Hashed representation of the provided data + */ + String generateHash(JsonNode data) { + Object node; + // if array or object, use the json string representation, otherwise get the real value + if (data.isContainerNode()) { + node = data.toString(); + } else if (data.isTextual()) { + node = data.asText(); + } else if (data.isInt()) { + node = data.asInt(); + } else if (data.isLong()) { + node = data.asLong(); + } else if (data.isDouble()) { + node = data.asDouble(); + } else if (data.isFloat()) { + node = data.floatValue(); + } else if (data.isBigInteger()) { + node = data.bigIntegerValue(); + } else if (data.isBigDecimal()) { + node = data.decimalValue(); + } else if (data.isBoolean()) { + node = data.asBoolean(); + } else node = data; // anything else + byte[] digest = hashAlgorithm.digest(node.toString().getBytes(StandardCharsets.UTF_8)); + return String.format("%032x", new BigInteger(1, digest)); + } + + /** + * Validate that the hashed payload matches data provided and stored data record + * + * @param data Payload + * @param dataRecord DataRecord instance + */ + private void validatePayload(JsonNode data, DataRecord dataRecord) throws IdempotencyValidationException { + if (payloadValidationEnabled) { + String dataHash = getHashedPayload(data); + if (!StringUtils.equals(dataHash, dataRecord.getPayloadHash())) { + throw new IdempotencyValidationException("Payload does not match stored record for this event key"); + } + } + } + + /** + * @param now + * @return unix timestamp of expiry date for idempotency record + */ + private long getExpiryEpochSecond(Instant now) { + return now.plus(expirationInSeconds, ChronoUnit.SECONDS).getEpochSecond(); + } + + /** + * Save data_record to local cache except when status is "INPROGRESS" + *
+ * NOTE: We can't cache "INPROGRESS" records as we have no way to reflect updates that can happen outside of the + * execution environment + * + * @param dataRecord DataRecord to save in cache + */ + private void saveToCache(DataRecord dataRecord) { + if (!useLocalCache) + return; + if (dataRecord.getStatus().equals(DataRecord.Status.INPROGRESS)) + return; + + cache.put(dataRecord.getIdempotencyKey(), dataRecord); + } + + private DataRecord retrieveFromCache(String idempotencyKey, Instant now) { + if (!useLocalCache) + return null; + + DataRecord record = cache.get(idempotencyKey); + if (record != null) { + if (!record.isExpired(now)) { + return record; + } + LOG.debug("Removing expired local cache record for idempotency key: {}", idempotencyKey); + deleteFromCache(idempotencyKey); + } + return null; + } + + private void deleteFromCache(String idempotencyKey) { + if (!useLocalCache) + return; + cache.remove(idempotencyKey); + } + + /** + * For test purpose only (adding a cache to mock) + */ + void configure(IdempotencyConfig config, String functionName, LRUCache cache) { + this.configure(config, functionName); + this.cache = cache; + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java new file mode 100644 index 000000000..b4f58a73d --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -0,0 +1,109 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import java.time.Instant; +import java.util.Objects; + +/** + * Data Class for idempotency records. This is actually the item that will be stored in the persistence layer. + */ +public class DataRecord { + private final String idempotencyKey; + private final String status; + private final long expiryTimestamp; + private final String responseData; + private final String payloadHash; + + public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash) { + this.idempotencyKey = idempotencyKey; + this.status = status.toString(); + this.expiryTimestamp = expiryTimestamp; + this.responseData = responseData; + this.payloadHash = payloadHash; + } + + public String getIdempotencyKey() { + return idempotencyKey; + } + + /** + * Check if data record is expired (based on expiration configured in the {@link software.amazon.lambda.powertools.idempotency.IdempotencyConfig}) + * + * @return Whether the record is currently expired or not + */ + public boolean isExpired(Instant now) { + return expiryTimestamp != 0 && now.isAfter(Instant.ofEpochSecond(expiryTimestamp)); + } + + public Status getStatus() { + Instant now = Instant.now(); + if (isExpired(now)) { + return Status.EXPIRED; + } else { + return Status.valueOf(status); + } + } + + public long getExpiryTimestamp() { + return expiryTimestamp; + } + + public String getResponseData() { + return responseData; + } + + public String getPayloadHash() { + return payloadHash; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DataRecord record = (DataRecord) o; + return expiryTimestamp == record.expiryTimestamp + && idempotencyKey.equals(record.idempotencyKey) + && status.equals(record.status) + && Objects.equals(responseData, record.responseData) + && Objects.equals(payloadHash, record.payloadHash); + } + + @Override + public int hashCode() { + return Objects.hash(idempotencyKey, status, expiryTimestamp, responseData, payloadHash); + } + + /** + * Status of the record: + *
    + *
  • INPROGRESS: record initialized when function starts
  • + *
  • COMPLETED: record updated with the result of the function when it ends
  • + *
  • EXPIRED: record expired, idempotency will not happen
  • + *
+ */ + public enum Status { + INPROGRESS("INPROGRESS"), COMPLETED("COMPLETED"), EXPIRED("EXPIRED"); + + private final String status; + + Status(String status) { + this.status = status; + } + + public String toString() { + return status; + } + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java new file mode 100644 index 000000000..6e36c6dc6 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -0,0 +1,357 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +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.DynamoDbClientBuilder; +import software.amazon.awssdk.services.dynamodb.model.*; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.lambda.powertools.idempotency.Constants; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; + +import java.time.Instant; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static software.amazon.lambda.powertools.idempotency.Constants.AWS_REGION_ENV; + +/** + * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.
+ * Use the {@link Builder} to create a new instance. + */ +public class DynamoDBPersistenceStore extends BasePersistenceStore implements PersistenceStore { + + private static final Logger LOG = LoggerFactory.getLogger(DynamoDBPersistenceStore.class); + + private final String tableName; + private final String keyAttr; + private final String staticPkValue; + private final String sortKeyAttr; + private final String expiryAttr; + private final String statusAttr; + private final String dataAttr; + private final String validationAttr; + private final DynamoDbClient dynamoDbClient; + + /** + * Private: use the {@link Builder} to instantiate a new {@link DynamoDBPersistenceStore} + */ + private DynamoDBPersistenceStore(String tableName, + String keyAttr, + String staticPkValue, + String sortKeyAttr, + String expiryAttr, + String statusAttr, + String dataAttr, + String validationAttr, + DynamoDbClient client) { + this.tableName = tableName; + this.keyAttr = keyAttr; + this.staticPkValue = staticPkValue; + this.sortKeyAttr = sortKeyAttr; + this.expiryAttr = expiryAttr; + this.statusAttr = statusAttr; + this.dataAttr = dataAttr; + this.validationAttr = validationAttr; + + if (client != null) { + this.dynamoDbClient = client; + } else { + DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv(AWS_REGION_ENV))); + this.dynamoDbClient = ddbBuilder.build(); + } + } + + @Override + public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { + GetItemResponse response = dynamoDbClient.getItem( + GetItemRequest.builder() + .tableName(tableName) + .key(getKey(idempotencyKey)) + .consistentRead(true) + .build() + ); + + if (!response.hasItem()) { + throw new IdempotencyItemNotFoundException(idempotencyKey); + } + + return itemToRecord(response.item()); + } + + @Override + public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlreadyExistsException { + Map item = new HashMap<>(getKey(record.getIdempotencyKey())); + item.put(this.expiryAttr, AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()); + item.put(this.statusAttr, AttributeValue.builder().s(record.getStatus().toString()).build()); + if (this.payloadValidationEnabled) { + item.put(this.validationAttr, AttributeValue.builder().s(record.getPayloadHash()).build()); + } + + try { + LOG.debug("Putting record for idempotency key: {}", record.getIdempotencyKey()); + + Map expressionAttributeNames = Stream.of( + new AbstractMap.SimpleEntry<>("#id", this.keyAttr), + new AbstractMap.SimpleEntry<>("#expiry", this.expiryAttr)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + dynamoDbClient.putItem( + PutItemRequest.builder() + .tableName(tableName) + .item(item) + .conditionExpression("attribute_not_exists(#id) OR #expiry < :now") + .expressionAttributeNames(expressionAttributeNames) + .expressionAttributeValues(Collections.singletonMap(":now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build())) + .build() + ); + } catch (ConditionalCheckFailedException e) { + LOG.debug("Failed to put record for already existing idempotency key: {}", record.getIdempotencyKey()); + throw new IdempotencyItemAlreadyExistsException("Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); + } + } + + @Override + public void updateRecord(DataRecord record) { + LOG.debug("Updating record for idempotency key: {}", record.getIdempotencyKey()); + String updateExpression = "SET #response_data = :response_data, #expiry = :expiry, #status = :status"; + + Map expressionAttributeNames = Stream.of( + new AbstractMap.SimpleEntry<>("#response_data", this.dataAttr), + new AbstractMap.SimpleEntry<>("#expiry", this.expiryAttr), + new AbstractMap.SimpleEntry<>("#status", this.statusAttr)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + Map expressionAttributeValues = Stream.of( + new AbstractMap.SimpleEntry<>(":response_data", AttributeValue.builder().s(record.getResponseData()).build()), + new AbstractMap.SimpleEntry<>(":expiry", AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()), + new AbstractMap.SimpleEntry<>(":status", AttributeValue.builder().s(record.getStatus().toString()).build())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + if (payloadValidationEnabled) { + updateExpression += ", #validation_key = :validation_key"; + expressionAttributeNames.put("#validation_key", this.validationAttr); + expressionAttributeValues.put(":validation_key", AttributeValue.builder().s(record.getPayloadHash()).build()); + } + + dynamoDbClient.updateItem(UpdateItemRequest.builder() + .tableName(tableName) + .key(getKey(record.getIdempotencyKey())) + .updateExpression(updateExpression) + .expressionAttributeNames(expressionAttributeNames) + .expressionAttributeValues(expressionAttributeValues) + .build() + ); + } + + @Override + public void deleteRecord(String idempotencyKey) { + LOG.debug("Deleting record for idempotency key: {}", idempotencyKey); + dynamoDbClient.deleteItem(DeleteItemRequest.builder() + .tableName(tableName) + .key(getKey(idempotencyKey)) + .build() + ); + } + + /** + * Get the key to use for requests (depending on if we have a sort key or not) + * + * @param idempotencyKey + * @return + */ + private Map getKey(String idempotencyKey) { + Map key = new HashMap<>(); + if (this.sortKeyAttr != null) { + key.put(this.keyAttr, AttributeValue.builder().s(this.staticPkValue).build()); + key.put(this.sortKeyAttr, AttributeValue.builder().s(idempotencyKey).build()); + } else { + key.put(this.keyAttr, AttributeValue.builder().s(idempotencyKey).build()); + } + return key; + } + + /** + * Translate raw item records from DynamoDB to DataRecord + * + * @param item Item from dynamodb response + * @return DataRecord instance + */ + private DataRecord itemToRecord(Map item) { + // data and validation payload may be null + AttributeValue data = item.get(this.dataAttr); + AttributeValue validation = item.get(this.validationAttr); + + return new DataRecord(item.get(sortKeyAttr != null ? sortKeyAttr: keyAttr).s(), + DataRecord.Status.valueOf(item.get(this.statusAttr).s()), + Long.parseLong(item.get(this.expiryAttr).n()), + data != null ? data.s() : null, + validation != null ? validation.s() : null); + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Use this builder to get an instance of {@link DynamoDBPersistenceStore}.
+ * With this builder you can configure the characteristics of the DynamoDB Table + * (name, key, sort key, and other field names).
+ * You can also set a custom {@link DynamoDbClient} for further tuning. + */ + public static class Builder { + private static final String funcEnv = System.getenv(Constants.LAMBDA_FUNCTION_NAME_ENV); + + private String tableName; + private String keyAttr = "id"; + private String staticPkValue = String.format("idempotency#%s", funcEnv != null ? funcEnv : ""); + private String sortKeyAttr; + private String expiryAttr = "expiration"; + private String statusAttr = "status"; + private String dataAttr = "data"; + private String validationAttr = "validation"; + private DynamoDbClient dynamoDbClient; + + /** + * Initialize and return a new instance of {@link DynamoDBPersistenceStore}.
+ * Example:
+ *
+         *     DynamoDBPersistenceStore.builder().withTableName("idempotency_store").build();
+         * 
+ * + * @return an instance of the {@link DynamoDBPersistenceStore} + */ + public DynamoDBPersistenceStore build() { + if (StringUtils.isEmpty(tableName)) { + throw new IllegalArgumentException("Table name is not specified"); + } + return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); + } + + /** + * Name of the table to use for storing execution records (mandatory) + * + * @param tableName Name of the DynamoDB table + * @return the builder instance (to chain operations) + */ + public Builder withTableName(String tableName) { + this.tableName = tableName; + return this; + } + + /** + * DynamoDB attribute name for partition key (optional), by default "id" + * + * @param keyAttr name of the key attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withKeyAttr(String keyAttr) { + this.keyAttr = keyAttr; + return this; + } + + /** + * DynamoDB attribute value for partition key (optional), by default "idempotency#[function-name]". + * This will be used if the {@link #sortKeyAttr} is set. + * + * @param staticPkValue name of the partition key attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withStaticPkValue(String staticPkValue) { + this.staticPkValue = staticPkValue; + return this; + } + + /** + * DynamoDB attribute name for the sort key (optional) + * + * @param sortKeyAttr name of the sort key attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withSortKeyAttr(String sortKeyAttr) { + this.sortKeyAttr = sortKeyAttr; + return this; + } + + /** + * DynamoDB attribute name for expiry timestamp (optional), by default "expiration" + * + * @param expiryAttr name of the expiry attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withExpiryAttr(String expiryAttr) { + this.expiryAttr = expiryAttr; + return this; + } + + /** + * DynamoDB attribute name for status (optional), by default "status" + * + * @param statusAttr name of the status attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withStatusAttr(String statusAttr) { + this.statusAttr = statusAttr; + return this; + } + + /** + * DynamoDB attribute name for response data (optional), by default "data" + * + * @param dataAttr name of the data attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withDataAttr(String dataAttr) { + this.dataAttr = dataAttr; + return this; + } + + /** + * DynamoDB attribute name for validation (optional), by default "validation" + * + * @param validationAttr name of the validation attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withValidationAttr(String validationAttr) { + this.validationAttr = validationAttr; + return this; + } + + /** + * Custom {@link DynamoDbClient} used to query DynamoDB (optional).
+ * The default one uses {@link UrlConnectionHttpClient} as a http client and + * add com.amazonaws.xray.interceptors.TracingInterceptor (X-Ray) if available in the classpath. + * + * @param dynamoDbClient the {@link DynamoDbClient} instance to use + * @return the builder instance (to chain operations) + */ + public Builder withDynamoDbClient(DynamoDbClient dynamoDbClient) { + this.dynamoDbClient = dynamoDbClient; + return this; + } + } +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java new file mode 100644 index 000000000..d199c99b5 --- /dev/null +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java @@ -0,0 +1,54 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +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}. + */ +public interface PersistenceStore { + + /** + * Retrieve item from persistence store using idempotency key and return it as a DataRecord instance. + * @param idempotencyKey the key of the record + * @return DataRecord representation of existing record found in persistence store + * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key + */ + DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException; + + /** + * Add a DataRecord to persistence store if it does not already exist with that key + * @param record DataRecord instance + * @param now + * @throws IdempotencyItemAlreadyExistsException if a non-expired entry already exists. + */ + void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlreadyExistsException; + + /** + * Update item in persistence store + * @param record DataRecord instance + */ + void updateRecord(DataRecord record); + + /** + * Remove item from persistence store + * @param idempotencyKey the key of the record + */ + void deleteRecord(String idempotencyKey); +} 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 new file mode 100644 index 000000000..38678322c --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java @@ -0,0 +1,79 @@ +package software.amazon.lambda.powertools.idempotency; + +import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; +import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; +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.*; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; + +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-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java new file mode 100644 index 000000000..a782d9613 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java @@ -0,0 +1,43 @@ +package software.amazon.lambda.powertools.idempotency; + + +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; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IdempotencyTest extends DynamoDBConfig { + + @Mock + private Context context; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void endToEndTest() { + IdempotencyFunction function = new IdempotencyFunction(client); + + 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); + assertThat(function.handlerExecuted).isFalse(); + + assertThat(response).isEqualTo(response2); + assertThat(response2.getBody()).contains("hello world"); + + assertThat(client.scan(ScanRequest.builder().tableName(TABLE_NAME).build()).count()).isEqualTo(1); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java new file mode 100644 index 000000000..6c39dc6de --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Simple Lambda function with @{@link Idempotent} annotation on handleRequest method + */ +public class IdempotencyEnabledFunction implements RequestHandler { + + private boolean handlerCalled = false; + + public boolean handlerCalled() { + return handlerCalled; + } + + @Override + @Idempotent + public Basket handleRequest(Product input, Context context) { + handlerCalled = true; + Basket b = new Basket(); + b.add(input); + return b; + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java new file mode 100644 index 000000000..c60336b81 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java @@ -0,0 +1,79 @@ +package software.amazon.lambda.powertools.idempotency.handlers; + +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.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 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; + +public class IdempotencyFunction implements RequestHandler { + private final static Logger LOG = LogManager.getLogger(); + + 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()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName("idempotency_table") + .withDynamoDbClient(client) + .build() + ).configure(); + } + + @Idempotent + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + handlerExecuted = true; + Map 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 { + 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); + } + } + + // 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/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java new file mode 100644 index 000000000..549d9e7ed --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.IdempotencyKey; +import software.amazon.lambda.powertools.idempotency.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Simple Lambda function with @{@link Idempotent} annotation on a sub method (not the handleRequest one) + */ +public class IdempotencyInternalFunction implements RequestHandler { + + private boolean called = false; + + @Override + public Basket handleRequest(Product input, Context context) { + return createBasket("fake", input); + } + + @Idempotent + private Basket createBasket(@IdempotencyKey String magicProduct, Product p) { + called = true; + Basket b = new Basket(p); + b.add(new Product(0, magicProduct, 0)); + return b; + } + + public boolean subMethodCalled() { + return called; + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java new file mode 100644 index 000000000..566db6727 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Simple Lambda function with @{@link Idempotent} annotation on a sub method (not the handleRequest one) + */ +public class IdempotencyInternalFunctionInternalKey implements RequestHandler { + + @Override + public Basket handleRequest(Product input, Context context) { + return createBasket(input); + } + + @Idempotent + private Basket createBasket(Product p) { + return new Basket(p); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java new file mode 100644 index 000000000..4c82bff15 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Simple Lambda function with @{@link Idempotent} annotation a sub method.
+ * This one is invalid as there are two parameters and @IdempotencyKey + * is not used to specify which one will be used as a key for persistence. + */ +public class IdempotencyInternalFunctionInvalid implements RequestHandler { + + @Override + public Basket handleRequest(Product input, Context context) { + return createBasket("fake", input); + } + + @Idempotent + private Basket createBasket(String magicProduct, Product p) { + Basket b = new Basket(p); + b.add(new Product(0, magicProduct, 0)); + return b; + } + +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java new file mode 100644 index 000000000..a6b89fc8d --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.IdempotencyKey; +import software.amazon.lambda.powertools.idempotency.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Simple Lambda function with @{@link Idempotent} annotation a sub method.
+ * This one is invalid because the annotated method return type is void, thus we cannot store any response. + */ +public class IdempotencyInternalFunctionVoid implements RequestHandler { + + @Override + public Basket handleRequest(Product input, Context context) { + Basket b = new Basket(input); + addProduct("fake", b); + return b; + } + + @Idempotent + private void addProduct(@IdempotencyKey String productName, Basket b) { + b.add(new Product(0, productName, 0)); + } + +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java new file mode 100644 index 000000000..1444d8a5f --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Simple Lambda function with @{@link Idempotent} annotation on handleRequest method.
+ * This function throws an exception. + */ +public class IdempotencyWithErrorFunction implements RequestHandler { + + @Override + @Idempotent + public Basket handleRequest(Product input, Context context) { + throw new IndexOutOfBoundsException("Fake exception"); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java new file mode 100644 index 000000000..fc91c6c61 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -0,0 +1,288 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import software.amazon.lambda.powertools.idempotency.Constants; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyAlreadyInProgressException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.handlers.*; +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.persistence.DataRecord; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +import java.time.Instant; + +import static java.time.temporal.ChronoUnit.SECONDS; +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.*; + +public class IdempotencyAspectTest { + + @Mock + private Context context; + + @Mock + private BasePersistenceStore store; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + public 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 nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + verify(store).saveInProgress(nodeCaptor.capture(), any()); + 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()); + + ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue()).isEqualTo(basket); + } + + @Test + public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build() + ).configure(); + + doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any()); + + Product p = new Product(42, "fake product", 12); + Basket b = new Basket(p); + DataRecord record = 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()); + + // WHEN + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + Basket basket = function.handleRequest(p, context); + + // THEN + assertThat(basket).isEqualTo(b); + assertThat(function.handlerCalled()).isFalse(); + } + + @Test + public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() throws JsonProcessingException { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build() + ).configure(); + + doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any()); + + Product p = new Product(42, "fake product", 12); + Basket b = new Basket(p); + DataRecord record = new DataRecord( + "42", + DataRecord.Status.INPROGRESS, + Instant.now().plus(356, SECONDS).getEpochSecond(), + JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), + null); + doReturn(record).when(store).getRecord(any(), any()); + + // THEN + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyAlreadyInProgressException.class); + } + + @Test + public void functionThrowException_shouldDeleteRecord_andThrowFunctionException() { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build() + ).configure(); + + // WHEN / THEN + IdempotencyWithErrorFunction function = new IdempotencyWithErrorFunction(); + + Product p = new Product(42, "fake product", 12); + assertThatThrownBy(() -> function.handleRequest(p, context)) + .isInstanceOf(IndexOutOfBoundsException.class); + + verify(store).deleteRecord(any(), any(IndexOutOfBoundsException.class)); + } + + @Test + @SetEnvironmentVariable(key = Constants.IDEMPOTENCY_DISABLED_ENV, value = "true") + public void testIdempotencyDisabled_shouldJustRunTheFunction() { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build() + ).configure(); + + // WHEN + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + + // THEN + verifyNoInteractions(store); + assertThat(basket.getProducts()).hasSize(1); + assertThat(function.handlerCalled()).isTrue(); + } + + @Test + public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + // WHEN + IdempotencyInternalFunction function = new IdempotencyInternalFunction(); + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + + // THEN + assertThat(basket.getProducts()).hasSize(2); + assertThat(function.subMethodCalled()).isTrue(); + + ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + verify(store).saveInProgress(nodeCaptor.capture(), any()); + assertThat(nodeCaptor.getValue().asText()).isEqualTo("fake"); + + ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), new Product(0, "fake", 0)); + } + + @Test + public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any()); + + Product p = new Product(42, "fake product", 12); + Basket b = new Basket(p); + DataRecord record = 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()); + + // WHEN + IdempotencyInternalFunction function = new IdempotencyInternalFunction(); + Basket basket = function.handleRequest(p, context); + + // THEN + assertThat(basket).isEqualTo(b); + assertThat(function.subMethodCalled()).isFalse(); + } + + @Test + public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey() { + BasePersistenceStore persistenceStore = spy(BasePersistenceStore.class); + + Idempotency.config() + .withPersistenceStore(persistenceStore) + .withConfig(IdempotencyConfig.builder().withEventKeyJMESPath("id").build()) + .configure(); + + // WHEN + IdempotencyInternalFunctionInternalKey function = new IdempotencyInternalFunctionInternalKey(); + Product p = new Product(42, "fake product", 12); + function.handleRequest(p, context); + + // THEN + ArgumentCaptor recordCaptor = ArgumentCaptor.forClass(DataRecord.class); + verify(persistenceStore).putRecord(recordCaptor.capture(), any()); + // a1d0c6e83f027327d8461063f4ac58a6 = MD5(42) + assertThat(recordCaptor.getValue().getIdempotencyKey()).isEqualTo("testFunction.createBasket#a1d0c6e83f027327d8461063f4ac58a6"); + } + + @Test + public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder().build() + ).configure(); + + // WHEN + IdempotencyInternalFunctionInvalid function = new IdempotencyInternalFunctionInvalid(); + Product p = new Product(42, "fake product", 12); + + // THEN + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); + } + + @Test + public void idempotencyOnSubMethodVoid_shouldThrowException() { + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder().build() + ).configure(); + + // WHEN + IdempotencyInternalFunctionVoid function = new IdempotencyInternalFunctionVoid(); + Product p = new Product(42, "fake product", 12); + + // THEN + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java new file mode 100644 index 000000000..3d2f7c7e3 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.cache; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LRUCacheTest { + + @Test + public void testLRUCache_shouldRemoveEldestEntry() { + LRUCache cache = new LRUCache<>(3); + cache.put("key1", "value1"); + cache.put("key2", "value2"); + cache.put("key3", "value3"); + cache.put("key4", "value4"); + cache.put("key5", "value5"); + + assertThat(cache).hasSize(3).doesNotContainKeys("key1", "key2"); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java new file mode 100644 index 000000000..304fd3810 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class Basket { + private List products = new ArrayList<>(); + + public List getProducts() { + return products; + } + + public void setProducts(List products) { + this.products = products; + } + + public Basket() { + } + + public Basket( Product ...p){ + products.addAll(Arrays.asList(p)); + } + + 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); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java new file mode 100644 index 000000000..1c66c584d --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java new file mode 100644 index 000000000..dac9a9288 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -0,0 +1,365 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +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 org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +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.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.idempotency.model.Product; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class BasePersistenceStoreTest { + + private DataRecord dr; + private BasePersistenceStore persistenceStore; + private int status = 0; + private String validationHash; + + @BeforeEach + public void setup() { + validationHash = null; + dr = null; + status = -1; + persistenceStore = new BasePersistenceStore() { + @Override + public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { + status = 0; + return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", validationHash); + } + + @Override + public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlreadyExistsException { + dr = record; + status = 1; + } + + @Override + public void updateRecord(DataRecord record) { + dr = record; + status = 2; + } + + @Override + public void deleteRecord(String idempotencyKey) { + dr = null; + status = 3; + } + }; + } + + // ================================================================= + // + @Test + public void saveInProgress_defaultConfig() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore.configure(IdempotencyConfig.builder().build(), null); + + Instant now = Instant.now(); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(status).isEqualTo(1); + } + + @Test + public void saveInProgress_jmespath() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore.configure(IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).id") + .build(), "myfunc"); + + Instant now = Instant.now(); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + 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.myfunc#2fef178cc82be5ce3da6c5e0466a6182"); + assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(status).isEqualTo(1); + } + + @Test + public void saveInProgress_jmespath_NotFound_shouldThrowException() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore.configure(IdempotencyConfig.builder() + .withEventKeyJMESPath("unavailable") + .withThrowOnNoIdempotencyKey(true) // should throw + .build(), ""); + Instant now = Instant.now(); + assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now)) + .isInstanceOf(IdempotencyKeyException.class) + .hasMessageContaining("No data found to create a hashed idempotency key"); + assertThat(status).isEqualTo(-1); + } + + @Test + public void saveInProgress_jmespath_NotFound_shouldNotThrowException() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore.configure(IdempotencyConfig.builder() + .withEventKeyJMESPath("unavailable") + .build(), ""); + Instant now = Instant.now(); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); + assertThat(status).isEqualTo(1); + } + + @Test + public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder() + .withUseLocalCache(true) + .withEventKeyJMESPath("powertools_json(body).id") + .build(), null, cache); + Instant now = Instant.now(); + cache.put("testFunction#2fef178cc82be5ce3da6c5e0466a6182", + new DataRecord( + "testFunction#2fef178cc82be5ce3da6c5e0466a6182", + DataRecord.Status.INPROGRESS, + now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), + null, null) + ); + assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now)) + .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + assertThat(status).isEqualTo(-1); + } + + @Test + public void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).id") + .withUseLocalCache(true) + .withExpiration(Duration.of(2, ChronoUnit.SECONDS)) + .build(), null, cache); + Instant now = Instant.now(); + cache.put("testFunction#2fef178cc82be5ce3da6c5e0466a6182", + new DataRecord( + "testFunction#2fef178cc82be5ce3da6c5e0466a6182", + DataRecord.Status.INPROGRESS, + now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), + null, null) + ); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); + assertThat(cache).isEmpty(); + assertThat(status).isEqualTo(1); + } + // + // ================================================================= + + // ================================================================= + // + + @Test + public void saveSuccess_shouldUpdateRecord() throws JsonProcessingException { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder().build(), null, cache); + + Product product = new Product(34543, "product", 42); + Instant now = Instant.now(); + persistenceStore.saveSuccess(JsonConfig.get().getObjectMapper().valueToTree(event), product, now); + + 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(status).isEqualTo(2); + assertThat(cache).isEmpty(); + } + + @Test + public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessingException { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder() + .withUseLocalCache(true).build(), null, cache); + + Product product = new Product(34543, "product", 42); + Instant now = Instant.now(); + persistenceStore.saveSuccess(JsonConfig.get().getObjectMapper().valueToTree(event), product, now); + + assertThat(status).isEqualTo(2); + assertThat(cache).hasSize(1); + DataRecord record = cache.get("testFunction#47261bd5b456f400f8d191cfb3a7482f"); + 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getPayloadHash()).isEqualTo(""); + } + + // + // ================================================================= + + // ================================================================= + // + + @Test + public void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); + assertThat(record.getResponseData()).isEqualTo("Response"); + assertThat(status).isEqualTo(0); + } + + @Test + public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder() + .withUseLocalCache(true).build(), "myfunc", cache); + + Instant now = Instant.now(); + DataRecord dr = new DataRecord( + "testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", + DataRecord.Status.COMPLETED, + now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), + "result of the function", + null); + cache.put("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", dr); + + DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); + assertThat(record.getResponseData()).isEqualTo("result of the function"); + assertThat(status).isEqualTo(-1); // getRecord must not be called (retrieve from cache) + } + + @Test + public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder() + .withUseLocalCache(true).build(), "myfunc", cache); + + Instant now = Instant.now(); + DataRecord dr = new DataRecord( + "testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", + DataRecord.Status.COMPLETED, + now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), + "result of the function", + null); + cache.put("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", dr); + + DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); + assertThat(record.getResponseData()).isEqualTo("Response"); + assertThat(status).isEqualTo(0); + assertThat(cache).isEmpty(); + } + + @Test + public 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(), + "myfunc"); + + this.validationHash = "different hash"; // "Lambda rocks" ==> 70c24d88041893f7fbab4105b76fd9e1 + + assertThatThrownBy(() -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) + .isInstanceOf(IdempotencyValidationException.class); + } + + // + // ================================================================= + + // ================================================================= + // + + @Test + public void deleteRecord_shouldDeleteRecordFromPersistence() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore.configure(IdempotencyConfig.builder().build(), null); + + persistenceStore.deleteRecord(JsonConfig.get().getObjectMapper().valueToTree(event), new ArithmeticException()); + assertThat(status).isEqualTo(3); + } + + @Test + public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + LRUCache cache = new LRUCache<>(2); + persistenceStore.configure(IdempotencyConfig.builder() + .withUseLocalCache(true).build(), null, cache); + + cache.put("testFunction#47261bd5b456f400f8d191cfb3a7482f", + new DataRecord("testFunction#47261bd5b456f400f8d191cfb3a7482f", DataRecord.Status.COMPLETED, 123, null, null)); + persistenceStore.deleteRecord(JsonConfig.get().getObjectMapper().valueToTree(event), new ArithmeticException()); + assertThat(status).isEqualTo(3); + assertThat(cache).isEmpty(); + } + + // + // ================================================================= + + @Test + public void generateHashString_shouldGenerateMd5ofString() { + persistenceStore.configure(IdempotencyConfig.builder().build(), null); + String expectedHash = "70c24d88041893f7fbab4105b76fd9e1"; // MD5(Lambda rocks) + String generatedHash = persistenceStore.generateHash(new TextNode("Lambda rocks")); + assertThat(generatedHash).isEqualTo(expectedHash); + } + + @Test + public 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}) + String generatedHash = persistenceStore.generateHash(JsonConfig.get().getObjectMapper().valueToTree(product)); + assertThat(generatedHash).isEqualTo(expectedHash); + } + + @Test + public void generateHashDouble_shouldGenerateMd5ofDouble() { + persistenceStore.configure(IdempotencyConfig.builder().build(), null); + String expectedHash = "bb84c94278119c8838649706df4db42b"; // MD5(256.42) + String generatedHash = persistenceStore.generateHash(new DoubleNode(256.42)); + assertThat(generatedHash).isEqualTo(expectedHash); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java new file mode 100644 index 000000000..ecf8ad3e0 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java @@ -0,0 +1,278 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.services.dynamodb.model.*; +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 java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * 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 { + protected static final String TABLE_NAME_CUSTOM = "idempotency_table_custom"; + private Map key; + private DynamoDBPersistenceStore dynamoDBPersistenceStore; + + // ================================================================= + // + @Test + public 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 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_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordAlreadyExist() { + key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); + + // GIVEN: Insert a fake item with same id + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.COMPLETED.toString()).build()); + item.put("data", AttributeValue.builder().s("Fake Data").build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + + // 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); + + // THEN: item was not updated, retrieve the initial one + Map 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)); + assertThat(itemInDb.get("data").s()).isEqualTo("Fake Data"); + } + + // + // ================================================================= + + // ================================================================= + // + + @Test + public void getRecord_shouldReturnExistingRecord() throws IdempotencyItemNotFoundException { + key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); + + // GIVEN: Insert a fake item with same id + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.COMPLETED.toString()).build()); + item.put("data", AttributeValue.builder().s("Fake Data").build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + + // WHEN + DataRecord record = 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); + } + + @Test + public void getRecord_shouldThrowException_whenRecordIsAbsent() { + assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")).isInstanceOf(IdempotencyItemNotFoundException.class); + } + + // + // ================================================================= + + // ================================================================= + // + + @Test + public void updateRecord_shouldUpdateRecord() { + // GIVEN: Insert a fake item with same id + key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.plus(360, ChronoUnit.SECONDS).getEpochSecond(); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + // enable payload validation + dynamoDBPersistenceStore.configure(IdempotencyConfig.builder().withPayloadValidationJMESPath("path").build(), null); + + // WHEN + expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); + DataRecord record = new DataRecord("key", DataRecord.Status.COMPLETED, expiry, "Fake result", "hash"); + dynamoDBPersistenceStore.updateRecord(record); + + // THEN + Map 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"); + } + + // + // ================================================================= + + // ================================================================= + // + + @Test + public void deleteRecord_shouldDeleteRecord() { + // GIVEN: Insert a fake item with same id + key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.plus(360, ChronoUnit.SECONDS).getEpochSecond(); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + assertThat(client.scan(ScanRequest.builder().tableName(TABLE_NAME).build()).count()).isEqualTo(1); + + // WHEN + dynamoDBPersistenceStore.deleteRecord("key"); + + // THEN + assertThat(client.scan(ScanRequest.builder().tableName(TABLE_NAME).build()).count()).isEqualTo(0); + } + + // + // ================================================================= + + @Test + public 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() + ) + .attributeDefinitions( + AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.S).build(), + AttributeDefinition.builder().attributeName("sortkey").attributeType(ScalarAttributeType.S).build() + ) + .billingMode(BillingMode.PAY_PER_REQUEST) + .build()); + + DynamoDBPersistenceStore persistenceStore = DynamoDBPersistenceStore.builder() + .withTableName(TABLE_NAME_CUSTOM) + .withDynamoDbClient(client) + .withDataAttr("result") + .withExpiryAttr("expiry") + .withKeyAttr("key") + .withSortKeyAttr("sortkey") + .withStaticPkValue("pk") + .withStatusAttr("state") + .withValidationAttr("valid") + .build(); + + Instant now = Instant.now(); + DataRecord record = new DataRecord( + "mykey", + DataRecord.Status.INPROGRESS, + now.plus(400, ChronoUnit.SECONDS).getEpochSecond(), + null, + null + ); + // PUT + persistenceStore.putRecord(record, now); + + Map customKey = new HashMap<>(); + customKey.put("key", AttributeValue.builder().s("pk").build()); + customKey.put("sortkey", AttributeValue.builder().s("mykey").build()); + + Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME_CUSTOM).key(customKey).build()).item(); + + // GET + DataRecord recordInDb = persistenceStore.getRecord("mykey"); + + assertThat(itemInDb).isNotNull(); + assertThat(itemInDb.get("key").s()).isEqualTo("pk"); + assertThat(itemInDb.get("sortkey").s()).isEqualTo(recordInDb.getIdempotencyKey()); + assertThat(itemInDb.get("state").s()).isEqualTo(recordInDb.getStatus().toString()); + assertThat(itemInDb.get("expiry").n()).isEqualTo(String.valueOf(recordInDb.getExpiryTimestamp())); + + // UPDATE + DataRecord updatedRecord = new DataRecord( + "mykey", + DataRecord.Status.COMPLETED, + now.plus(500, ChronoUnit.SECONDS).getEpochSecond(), + "response", + null + ); + persistenceStore.updateRecord(updatedRecord); + recordInDb = persistenceStore.getRecord("mykey"); + assertThat(recordInDb).isEqualTo(updatedRecord); + + // DELETE + persistenceStore.deleteRecord("mykey"); + assertThat(client.scan(ScanRequest.builder().tableName(TABLE_NAME_CUSTOM).build()).count()).isEqualTo(0); + + } finally { + try { + client.deleteTable(DeleteTableRequest.builder().tableName(TABLE_NAME_CUSTOM).build()); + } catch (Exception e) { + // OK + } + } + } + + @BeforeEach + public void setup() { + dynamoDBPersistenceStore = DynamoDBPersistenceStore.builder() + .withTableName(TABLE_NAME) + .withDynamoDbClient(client) + .build(); + } + + @AfterEach + public void emptyDB() { + if (key != null) { + client.deleteItem(DeleteItemRequest.builder().tableName(TABLE_NAME).key(key).build()); + key = null; + } + } +} diff --git a/powertools-idempotency/src/test/resources/apigw_event.json b/powertools-idempotency/src/test/resources/apigw_event.json new file mode 100644 index 000000000..4f5f95db0 --- /dev/null +++ b/powertools-idempotency/src/test/resources/apigw_event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"Lambda rocks\", \"id\": 43876123454654}", + "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/powertools-idempotency/src/test/resources/apigw_event2.json b/powertools-idempotency/src/test/resources/apigw_event2.json new file mode 100644 index 000000000..a313815c1 --- /dev/null +++ b/powertools-idempotency/src/test/resources/apigw_event2.json @@ -0,0 +1,62 @@ +{ + "body": "{\"address\": \"https://checkip.amazonaws.com\"}", + "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/powertools-serialization/pom.xml b/powertools-serialization/pom.xml new file mode 100644 index 000000000..e4192d66a --- /dev/null +++ b/powertools-serialization/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + + powertools-parent + software.amazon.lambda + 1.10.3 + + + powertools-serialization + jar + + AWS Lambda Powertools Java library Serialization Utilities + + + + https://aws.amazon.com/lambda/ + + GitHub Issues + https://github.com/awslabs/aws-lambda-powertools-java/issues + + + https://github.com/awslabs/aws-lambda-powertools-java.git + + + + AWS Lambda Powertools team + Amazon Web Services + https://aws.amazon.com/ + + + + + + ossrh + https://aws.oss.sonatype.org/content/repositories/snapshots + + + + + + io.burt + jmespath-jackson + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.assertj + assertj-core + test + + + + + + + org.codehaus.mojo + aspectj-maven-plugin + ${aspectj-maven-plugin.version} + + true + + + + + + \ No newline at end of file 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 new file mode 100644 index 000000000..c3a5fc865 --- /dev/null +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java @@ -0,0 +1,92 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.burt.jmespath.JmesPath; +import io.burt.jmespath.RuntimeConfiguration; +import io.burt.jmespath.function.BaseFunction; +import io.burt.jmespath.function.FunctionRegistry; +import io.burt.jmespath.jackson.JacksonRuntime; +import software.amazon.lambda.powertools.utilities.jmespath.Base64Function; +import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; +import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; + +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; + +public class JsonConfig { + private JsonConfig() { + } + + private static class ConfigHolder { + private final static JsonConfig instance = new JsonConfig(); + } + + public static JsonConfig get() { + return ConfigHolder.instance; + } + + private static final ThreadLocal om = ThreadLocal.withInitial(() -> { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); + return objectMapper; + }); + + private final FunctionRegistry defaultFunctions = FunctionRegistry.defaultRegistry(); + private final FunctionRegistry customFunctions = defaultFunctions.extend( + new Base64Function(), + new Base64GZipFunction(), + new JsonFunction() + ); + private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() + .withFunctionRegistry(customFunctions) + .build(); + private JmesPath jmesPath = new JacksonRuntime(configuration, getObjectMapper()); + + /** + * Return an Object Mapper. Use this to customize (de)serialization config. + * + * @return the {@link ObjectMapper} to serialize / deserialize JSON + */ + public ObjectMapper getObjectMapper() { + return om.get(); + } + + /** + * Return the JmesPath used to select sub node of Json + * + * @return the {@link JmesPath} + */ + public JmesPath getJmesPath() { + return jmesPath; + } + + /** + * Add a custom {@link io.burt.jmespath.function.Function} to JMESPath + * {@link Base64Function} and {@link Base64GZipFunction} are already built-in. + * + * @param function the function to add + * @param Must extends {@link BaseFunction} + */ + public void addFunction(T function) { + FunctionRegistry functionRegistryWithExtendedFunctions = configuration.functionRegistry().extend(function); + + RuntimeConfiguration updatedConfig = new RuntimeConfiguration.Builder() + .withFunctionRegistry(functionRegistryWithExtendedFunctions) + .build(); + + jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); + } +} diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/jmespath/Base64Function.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java similarity index 94% rename from powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/jmespath/Base64Function.java rename to powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java index c5693f8a7..737d96835 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/jmespath/Base64Function.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2022 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,7 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.validation.jmespath; - -import java.nio.ByteBuffer; -import java.util.Base64; -import java.util.List; +package software.amazon.lambda.powertools.utilities.jmespath; import io.burt.jmespath.Adapter; import io.burt.jmespath.JmesPathType; @@ -23,6 +19,10 @@ import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; +import java.nio.ByteBuffer; +import java.util.Base64; +import java.util.List; + import static java.nio.charset.StandardCharsets.UTF_8; /** diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/jmespath/Base64GZipFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java similarity index 92% rename from powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/jmespath/Base64GZipFunction.java rename to powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java index bd4b338c4..6b097af62 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/jmespath/Base64GZipFunction.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2022 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,7 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.validation.jmespath; +package software.amazon.lambda.powertools.utilities.jmespath; + +import io.burt.jmespath.Adapter; +import io.burt.jmespath.JmesPathType; +import io.burt.jmespath.function.ArgumentConstraints; +import io.burt.jmespath.function.BaseFunction; +import io.burt.jmespath.function.FunctionArgument; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -21,14 +27,8 @@ import java.util.List; import java.util.zip.GZIPInputStream; -import io.burt.jmespath.Adapter; -import io.burt.jmespath.JmesPathType; -import io.burt.jmespath.function.ArgumentConstraints; -import io.burt.jmespath.function.BaseFunction; -import io.burt.jmespath.function.FunctionArgument; - import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.validation.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; /** * Function used by JMESPath to decode a Base64 encoded GZipped String into a decoded String diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java new file mode 100644 index 000000000..584b544bf --- /dev/null +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities.jmespath; + +import io.burt.jmespath.Adapter; +import io.burt.jmespath.JmesPathType; +import io.burt.jmespath.function.ArgumentConstraints; +import io.burt.jmespath.function.BaseFunction; +import io.burt.jmespath.function.FunctionArgument; + +import java.util.List; + +public class JsonFunction extends BaseFunction { + + public JsonFunction() { + super("powertools_json", ArgumentConstraints.typeOf(JmesPathType.STRING)); + } + + @Override + protected T callFunction(Adapter runtime, List> arguments) { + T value = arguments.get(0).value(); + String jsonString = runtime.toString(value); + return runtime.parseString(jsonString); + } +} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/Base64FunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java similarity index 77% rename from powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/Base64FunctionTest.java rename to powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java index b9bbd6f88..5f243537c 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/Base64FunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java @@ -11,12 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.validation; +package software.amazon.lambda.powertools.utilities.jmespath; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import io.burt.jmespath.Expression; import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.utilities.JsonConfig; import java.io.IOException; @@ -26,8 +27,8 @@ public class Base64FunctionTest { @Test public void testPowertoolsBase64() throws IOException { - JsonNode event = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event.json")); - Expression expression = ValidationConfig.get().getJmesPath().compile("basket.powertools_base64(hiddenProduct)"); + JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event.json")); + Expression 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-validation/src/test/java/software/amazon/lambda/powertools/validation/Base64GZipFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java similarity index 75% rename from powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/Base64GZipFunctionTest.java rename to powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java index 4fc0e57c5..8c617a634 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/Base64GZipFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java @@ -11,12 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.validation; +package software.amazon.lambda.powertools.utilities.jmespath; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import io.burt.jmespath.Expression; import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.utilities.JsonConfig; import java.io.IOException; @@ -26,8 +27,8 @@ public class Base64GZipFunctionTest { @Test public void testPowertoolsGzip() throws IOException { - JsonNode event = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression expression = ValidationConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(hiddenProduct)"); + JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + Expression 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}"); 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 new file mode 100644 index 000000000..4ea4eed35 --- /dev/null +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java @@ -0,0 +1,34 @@ +package software.amazon.lambda.powertools.utilities.jmespath; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import io.burt.jmespath.Expression; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JsonFunctionTest { + + @Test + public void testJsonFunction() throws IOException { + JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); + Expression expression = JsonConfig.get().getJmesPath().compile("powertools_json(body)"); + JsonNode result = expression.search(event); + assertThat(result.getNodeType()).isEqualTo(JsonNodeType.OBJECT); + assertThat(result.get("message").asText()).isEqualTo("Lambda rocks"); + assertThat(result.get("list").isArray()).isTrue(); + assertThat(result.get("list").size()).isEqualTo(2); + } + + @Test + public void testJsonFunctionChild() throws IOException { + JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); + Expression expression = JsonConfig.get().getJmesPath().compile("powertools_json(body).list[0].item"); + JsonNode result = expression.search(event); + assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); + assertThat(result.asText()).isEqualTo("4gh345h"); + } +} diff --git a/powertools-serialization/src/test/resources/custom_event.json b/powertools-serialization/src/test/resources/custom_event.json new file mode 100644 index 000000000..13103c434 --- /dev/null +++ b/powertools-serialization/src/test/resources/custom_event.json @@ -0,0 +1,12 @@ +{ + "basket": { + "products" : [ + { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + ], + "hiddenProduct": "ewogICJpZCI6IDQzMjQyLAogICJuYW1lIjogIkZvb0JhciBYWSIsCiAgInByaWNlIjogMjU4Cn0=" + } +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/custom_event_gzip.json b/powertools-serialization/src/test/resources/custom_event_gzip.json new file mode 100644 index 000000000..d212052d0 --- /dev/null +++ b/powertools-serialization/src/test/resources/custom_event_gzip.json @@ -0,0 +1,12 @@ +{ + "basket": { + "products" : [ + { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + ], + "hiddenProduct": "H4sIAAAAAAAA/6vmUlBQykxRslIwMTYyMdIBcfMSc1OBAkpu+flOiUUKEZFKYOGCosxkkLiRqQVXLQDnWo6bOAAAAA==" + } +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/custom_event_json.json b/powertools-serialization/src/test/resources/custom_event_json.json new file mode 100644 index 000000000..edc8fa298 --- /dev/null +++ b/powertools-serialization/src/test/resources/custom_event_json.json @@ -0,0 +1,3 @@ +{ + "body": "{\"message\": \"Lambda rocks\", \"list\":[{\"item\":\"4gh345h\", \"price\":42}, {\"item\":\"45jk6h46\", \"price\":24}]}" +} diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index ba0723e41..aaabe88b7 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -46,6 +46,10 @@ software.amazon.lambda powertools-core
+ + software.amazon.lambda + powertools-serialization + com.amazonaws aws-lambda-java-events 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 191c50107..3fd964226 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 @@ -18,18 +18,17 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import io.burt.jmespath.JmesPath; -import io.burt.jmespath.RuntimeConfiguration; import io.burt.jmespath.function.BaseFunction; -import io.burt.jmespath.function.FunctionRegistry; -import io.burt.jmespath.jackson.JacksonRuntime; -import software.amazon.lambda.powertools.validation.jmespath.Base64Function; -import software.amazon.lambda.powertools.validation.jmespath.Base64GZipFunction; - -import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; +import software.amazon.lambda.powertools.utilities.JsonConfig; +import software.amazon.lambda.powertools.utilities.jmespath.Base64Function; +import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; /** * Use this if you need to customize some part of the JSON Schema validation - * (eg. specification version, Jackson ObjectMapper, or adding functions to JMESPath) + * (eg. specification version, Jackson ObjectMapper, or adding functions to JMESPath). + * + * For everything but the validation features (factory, schemaVersion), {@link ValidationConfig} + * is just a wrapper of {@link JsonConfig}. */ public class ValidationConfig { private ValidationConfig() { @@ -43,24 +42,9 @@ public static ValidationConfig get() { return ConfigHolder.instance; } - private static final ThreadLocal om = ThreadLocal.withInitial(() -> { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); - return objectMapper; - }); - private SpecVersion.VersionFlag jsonSchemaVersion = SpecVersion.VersionFlag.V7; private JsonSchemaFactory factory = JsonSchemaFactory.getInstance(jsonSchemaVersion); - private final FunctionRegistry defaultFunctions = FunctionRegistry.defaultRegistry(); - private final FunctionRegistry customFunctions = defaultFunctions.extend( - new Base64Function(), - new Base64GZipFunction()); - private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() - .withFunctionRegistry(customFunctions) - .build(); - private JmesPath jmesPath = new JacksonRuntime(configuration, getObjectMapper()); - /** * Set the version of the json schema specifications (default is V7) * @@ -85,13 +69,7 @@ public SpecVersion.VersionFlag getSchemaVersion() { * @param Must extends {@link BaseFunction} */ public void addFunction(T function) { - FunctionRegistry functionRegistryWithExtendedFunctions = configuration.functionRegistry().extend(function); - - RuntimeConfiguration updatedConfig = new RuntimeConfiguration.Builder() - .withFunctionRegistry(functionRegistryWithExtendedFunctions) - .build(); - - jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); + JsonConfig.get().addFunction(function); } /** @@ -109,7 +87,7 @@ public JsonSchemaFactory getFactory() { * @return the {@link JmesPath} */ public JmesPath getJmesPath() { - return jmesPath; + return JsonConfig.get().getJmesPath(); } /** @@ -118,6 +96,6 @@ public JmesPath getJmesPath() { * @return the {@link ObjectMapper} to serialize / deserialize JSON */ public ObjectMapper getObjectMapper() { - return om.get(); + return JsonConfig.get().getObjectMapper(); } } 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 b665ca2e0..b42ce71ab 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 @@ -26,10 +26,10 @@ import static java.nio.charset.StandardCharsets.UTF_8; 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.utilities.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; -import static software.amazon.lambda.powertools.validation.jmespath.Base64Function.decode; -import static software.amazon.lambda.powertools.validation.jmespath.Base64GZipFunction.decompress; /** * Aspect for {@link Validation} annotation diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index b36b180cb..30e627a56 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -28,6 +28,10 @@ + + + + @@ -61,6 +65,18 @@ + + + + + + + + + + + + From 44455c2ae4739936b39d0cc5a6c3cd05ecd05b37 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Wed, 16 Feb 2022 18:56:44 +0100 Subject: [PATCH 0042/1008] chore:Prep release 1.11.0 (#753) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore:prep release 1.11.0 * Update CHANGELOG.md Co-authored-by: Jérôme Van Der Linden Co-authored-by: pankajagrawal16 Co-authored-by: Jérôme Van Der Linden --- CHANGELOG.md | 7 +++++++ README.md | 6 +++--- example/HelloWorldFunction/build.gradle | 16 ++++++++-------- example/HelloWorldFunction/pom.xml | 14 +++++++------- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 17 files changed, 38 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 299716087..1112f04da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.11.0] - 2022-02-16 + +### Added + * Powertools Idempotency module: New module to get your Lambda function [Idempotent](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) (#717) + * Powertools Serialization module: New module to handle JSON (de)serialization (Jackson ObjectMapper, JMESPath functions) + + ## [1.10.3] - 2022-02-01 ### Bug Fixes diff --git a/README.md b/README.md index 111b31af6..09c0f52be 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.10.3 + 1.11.0 software.amazon.lambda powertools-logging - 1.10.3 + 1.11.0 software.amazon.lambda powertools-metrics - 1.10.3 + 1.11.0 ... diff --git a/example/HelloWorldFunction/build.gradle b/example/HelloWorldFunction/build.gradle index b342a4931..b1a266606 100644 --- a/example/HelloWorldFunction/build.gradle +++ b/example/HelloWorldFunction/build.gradle @@ -9,15 +9,15 @@ repositories { } dependencies { - aspect 'software.amazon.lambda:powertools-logging:1.10.3' - aspect 'software.amazon.lambda:powertools-tracing:1.10.3' - aspect 'software.amazon.lambda:powertools-metrics:1.10.3' - aspect 'software.amazon.lambda:powertools-sqs:1.10.3' - aspect 'software.amazon.lambda:powertools-parameters:1.10.3' - aspect 'software.amazon.lambda:powertools-validation:1.10.3' + aspect 'software.amazon.lambda:powertools-logging:1.11.0' + aspect 'software.amazon.lambda:powertools-tracing:1.11.0' + aspect 'software.amazon.lambda:powertools-metrics:1.11.0' + aspect 'software.amazon.lambda:powertools-sqs:1.11.0' + aspect 'software.amazon.lambda:powertools-parameters:1.11.0' + aspect 'software.amazon.lambda:powertools-validation:1.11.0' - implementation 'software.amazon.lambda:powertools-idempotency:1.10.3' - aspectpath 'software.amazon.lambda:powertools-idempotency:1.10.3' + implementation 'software.amazon.lambda:powertools-idempotency:1.11.0' + aspectpath 'software.amazon.lambda:powertools-idempotency:1.11.0' implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' diff --git a/example/HelloWorldFunction/pom.xml b/example/HelloWorldFunction/pom.xml index 96786e152..1d5b826a3 100644 --- a/example/HelloWorldFunction/pom.xml +++ b/example/HelloWorldFunction/pom.xml @@ -16,37 +16,37 @@ software.amazon.lambda powertools-tracing - 1.10.3 + 1.11.0 software.amazon.lambda powertools-logging - 1.10.3 + 1.11.0 software.amazon.lambda powertools-metrics - 1.10.3 + 1.11.0 software.amazon.lambda powertools-parameters - 1.10.3 + 1.11.0 software.amazon.lambda powertools-validation - 1.10.3 + 1.11.0 software.amazon.lambda powertools-sqs - 1.10.3 + 1.11.0 software.amazon.lambda powertools-idempotency - 1.10.3 + 1.11.0 com.amazonaws diff --git a/mkdocs.yml b/mkdocs.yml index 0881cdc5c..59b02d22c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,7 +77,7 @@ extra_javascript: extra: powertools: - version: 1.10.3 + version: 1.11.0 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index ecc2b305f..8ba4031e0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.10.3 + 1.11.0 pom AWS Lambda Powertools Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 9264f446d..7414e4576 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 5c5034c51..49bd90f31 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 785447d56..7db7ed3a4 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.10.3 + 1.11.0 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index b6e6963ed..1750993eb 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 609f0da8c..604b69e45 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index ffb982f22..9a18a6788 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e4192d66a..5cff32313 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 6bb325856..7b187f0c3 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index b8d60b6e3..ce6677e7b 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index be476dd48..5d5cb1e2c 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index aaabe88b7..db73fb12d 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.10.3 + 1.11.0 AWS Lambda Powertools Java validation library From 664533c55aff8f3460988ef4e614d32faa66b734 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 17 Feb 2022 09:11:33 +0100 Subject: [PATCH 0043/1008] chore: downgrade release plugin to validate release fail issue --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8ba4031e0..bbb1f22ef 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 2.22.2 0.8.7 2.7 - 1.6.11 + 1.6.10 3.3.2 3.2.1 3.0.1 From f99702d0c49b9555035eb8d72dbc6d34ba3edd89 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 17 Feb 2022 09:15:14 +0100 Subject: [PATCH 0044/1008] chore: downgrade release plugin to validate release fail issue --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bbb1f22ef..3e1892e95 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 2.22.2 0.8.7 2.7 - 1.6.10 + 1.6.8 3.3.2 3.2.1 3.0.1 From 363d0d93be7906a9c65ec88dd1b876b1a7320e47 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 17 Feb 2022 10:58:17 +0100 Subject: [PATCH 0045/1008] chore: remove SQS and Idempotency examples (#754) --- example/HelloWorldFunction/build.gradle | 7 -- example/HelloWorldFunction/pom.xml | 41 --------- .../main/java/helloworld/AppIdempotency.java | 84 ------------------- .../src/main/java/helloworld/AppSqsEvent.java | 35 -------- .../main/java/helloworld/AppSqsEventUtil.java | 39 --------- example/template.yaml | 79 +---------------- 6 files changed, 1 insertion(+), 284 deletions(-) delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppSqsEvent.java delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppSqsEventUtil.java diff --git a/example/HelloWorldFunction/build.gradle b/example/HelloWorldFunction/build.gradle index b1a266606..18166b5f3 100644 --- a/example/HelloWorldFunction/build.gradle +++ b/example/HelloWorldFunction/build.gradle @@ -9,16 +9,9 @@ repositories { } dependencies { - aspect 'software.amazon.lambda:powertools-logging:1.11.0' - aspect 'software.amazon.lambda:powertools-tracing:1.11.0' - aspect 'software.amazon.lambda:powertools-metrics:1.11.0' - aspect 'software.amazon.lambda:powertools-sqs:1.11.0' aspect 'software.amazon.lambda:powertools-parameters:1.11.0' aspect 'software.amazon.lambda:powertools-validation:1.11.0' - implementation 'software.amazon.lambda:powertools-idempotency:1.11.0' - aspectpath 'software.amazon.lambda:powertools-idempotency:1.11.0' - implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' diff --git a/example/HelloWorldFunction/pom.xml b/example/HelloWorldFunction/pom.xml index 1d5b826a3..51a6a765d 100644 --- a/example/HelloWorldFunction/pom.xml +++ b/example/HelloWorldFunction/pom.xml @@ -13,21 +13,6 @@ - - software.amazon.lambda - powertools-tracing - 1.11.0 - - - software.amazon.lambda - powertools-logging - 1.11.0 - - - software.amazon.lambda - powertools-metrics - 1.11.0 - software.amazon.lambda powertools-parameters @@ -38,16 +23,6 @@ powertools-validation 1.11.0 - - software.amazon.lambda - powertools-sqs - 1.11.0 - - - software.amazon.lambda - powertools-idempotency - 1.11.0 - com.amazonaws aws-lambda-java-core @@ -93,22 +68,6 @@ ${maven.compiler.target} ${maven.compiler.target} - - software.amazon.lambda - powertools-tracing - - - software.amazon.lambda - powertools-logging - - - software.amazon.lambda - powertools-metrics - - - software.amazon.lambda - powertools-sqs - software.amazon.lambda powertools-validation diff --git a/example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java b/example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java deleted file mode 100644 index 64b5d79a5..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/AppIdempotency.java +++ /dev/null @@ -1,84 +0,0 @@ -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 org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -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 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; - -public class AppIdempotency implements RequestHandler { - private final static Logger LOG = LogManager.getLogger(); - - public AppIdempotency() { - // we need to initialize idempotency configuration before the handleRequest method is called - Idempotency.config().withConfig( - IdempotencyConfig.builder() - .withEventKeyJMESPath("powertools_json(body).address") - .build()) - .withPersistenceStore( - DynamoDBPersistenceStore.builder() - .withTableName("idempotency_table") - .build() - ).configure(); - } - - - /** - * Try with: - *
-     *     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"}'
-     * 
- * @param input - * @param context - * @return - */ - @Idempotent - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - Map 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 { - 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); - } - } - - // 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/example/HelloWorldFunction/src/main/java/helloworld/AppSqsEvent.java b/example/HelloWorldFunction/src/main/java/helloworld/AppSqsEvent.java deleted file mode 100644 index 31ba1f760..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/AppSqsEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -package helloworld; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.sqs.SqsBatch; -import software.amazon.lambda.powertools.sqs.SqsMessageHandler; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; - -public class AppSqsEvent implements RequestHandler { - private static final Logger log = LogManager.getLogger(AppSqsEvent.class); - - @SqsBatch(SampleMessageHandler.class) - @Logging(logEvent = true) - @Override - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public static class SampleMessageHandler implements SqsMessageHandler { - - @Override - public String process(SQSMessage message) { - if("19dd0b57-b21e-4ac1-bd88-01bbb068cb99".equals(message.getMessageId())) { - throw new RuntimeException(message.getMessageId()); - } - log.info("Processing message with details {}", message); - return message.getMessageId(); - } - } -} diff --git a/example/HelloWorldFunction/src/main/java/helloworld/AppSqsEventUtil.java b/example/HelloWorldFunction/src/main/java/helloworld/AppSqsEventUtil.java deleted file mode 100644 index 3ececdfe1..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/AppSqsEventUtil.java +++ /dev/null @@ -1,39 +0,0 @@ -package helloworld; - -import java.util.List; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.sqs.SqsUtils; -import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; - -import static java.util.Collections.emptyList; - -public class AppSqsEventUtil implements RequestHandler> { - private static final Logger log = LogManager.getLogger(AppSqsEventUtil.class); - - @Override - public List handleRequest(SQSEvent input, Context context) { - try { - - return SqsUtils.batchProcessor(input, (message) -> { - if ("19dd0b57-b21e-4ac1-bd88-01bbb068cb99".equals(message.getMessageId())) { - throw new RuntimeException(message.getMessageId()); - } - - log.info("Processing message with details {}", message); - return message.getMessageId(); - }); - - } catch (SQSBatchProcessingException e) { - log.info("Exception details {}", e.getMessage(), e); - log.info("Success message Returns{}", e.successMessageReturnValues()); - log.info("Failed messages {}", e.getFailures()); - log.info("Failed messages Reasons {}", e.getExceptions()); - return emptyList(); - } - } -} diff --git a/example/template.yaml b/example/template.yaml index 901d8306d..907ae13b1 100644 --- a/example/template.yaml +++ b/example/template.yaml @@ -61,27 +61,6 @@ Resources: Name: id Type: String - HelloWorldIdempotentFunction: - 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: HelloWorldFunction - Handler: helloworld.AppIdempotency::handleRequest - 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_LOG_LEVEL: INFO - AWS_ENDPOINT_DISCOVERY_ENABLED: false - Tracing: Active - Policies: - - DynamoDBCrudPolicy: - TableName: !Ref IdempotencyTable - 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: /helloidem - Method: post - UserPwd: Type: AWS::SecretsManager::Secret Properties: @@ -131,55 +110,6 @@ Resources: Value: aGVsbG8gd29ybGQ= Description: Base64 SSM Parameter for lambda-powertools-java powertools-parameters module - TestSqsQueue: - Type: AWS::SQS::Queue - - HelloWorldSqsEventFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: HelloWorldFunction - Handler: helloworld.AppSqsEvent::handleRequest - MemorySize: 512 - Tracing: Active - Policies: - - Statement: - - Sid: AdditionalPermisssionForPowertoolsSQSUtils - Effect: Allow - Action: - - sqs:GetQueueUrl - - sqs:DeleteMessageBatch - Resource: !GetAtt TestSqsQueue.Arn - Events: - TestSQSEvent: - Type: SQS - Properties: - Queue: !GetAtt TestSqsQueue.Arn - BatchSize: 10 - - TestAnotherSqsQueue: - Type: AWS::SQS::Queue - - HelloWorldSqsEventUtilFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: HelloWorldFunction - Handler: helloworld.AppSqsEventUtil::handleRequest - MemorySize: 512 - Tracing: Active - Policies: - - Statement: - - Sid: AdditionalPermisssionForPowertoolsSQSUtils - Effect: Allow - Action: - - sqs:GetQueueUrl - - sqs:DeleteMessageBatch - Resource: !GetAtt TestAnotherSqsQueue.Arn - Events: - TestSQSEvent: - Type: SQS - Properties: - Queue: !GetAtt TestAnotherSqsQueue.Arn - BatchSize: 10 Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function @@ -198,11 +128,4 @@ Outputs: Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/helloparams/" HelloWorldParamsFunction: Description: "Hello World Params Lambda Function ARN" - Value: !GetAtt HelloWorldParamsFunction.Arn - - HelloWorldIdempotencyApi: - Description: "API Gateway endpoint URL for Prod stage for Hello World idempotency function" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/helloidem/" - HelloWorldIdempotencyFunction: - Description: "Hello World Idempotency Lambda Function ARN" - Value: !GetAtt HelloWorldIdempotentFunction.Arn + Value: !GetAtt HelloWorldParamsFunction.Arn \ No newline at end of file From 86aa4f2c474cd611fcdc0cc3ef743f381ae8b413 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 12:11:38 +0100 Subject: [PATCH 0046/1008] build(deps): bump maven-jar-plugin from 3.2.0 to 3.2.2 (#756) Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.0 to 3.2.2. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.0...maven-jar-plugin-3.2.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7db7ed3a4..d067c9f96 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -170,7 +170,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.2.2 From 53fa8e8076a3ea29b59e63b68e33165ba5b46785 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 12:11:59 +0100 Subject: [PATCH 0047/1008] build(deps): bump aws.sdk.version from 2.17.130 to 2.17.131 (#755) Bumps `aws.sdk.version` from 2.17.130 to 2.17.131. Updates `software.amazon.awssdk:bom` from 2.17.130 to 2.17.131 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.130...2.17.131) Updates `http-client-spi` from 2.17.130 to 2.17.131 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.130...2.17.131) Updates `url-connection-client` from 2.17.130 to 2.17.131 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3e1892e95..e2eb5feba 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.130 + 2.17.131 2.11.0 1.1.1 UTF-8 From 8370375a9dd374ef0cf7163a998fd00f3ba71668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Fri, 18 Feb 2022 05:29:19 +0100 Subject: [PATCH 0048/1008] fix #758 (#759) remove trailing slash for multiple params --- .../powertools/parameters/BaseProvider.java | 10 ++++---- .../parameters/SSMProviderTest.java | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) 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 706b555c1..fb539f850 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 @@ -123,13 +123,15 @@ public BaseProvider withTransformation(Class transformerC */ @Override public Map getMultiple(String path) { + // remove trailing whitespace + String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { - return (Map) cacheManager.getIfNotExpired(path, now()).orElseGet(() -> { - Map params = getMultipleValues(path); + return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> { + Map params = getMultipleValues(pathWithoutTrailingSlash); - cacheManager.putInCache(path, params); + cacheManager.putInCache(pathWithoutTrailingSlash, params); - params.forEach((k, v) -> cacheManager.putInCache(path + "/" + k, v)); + params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); return params; }); diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java index 761979e00..da299c38d 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java @@ -103,6 +103,29 @@ public void getMultiple() { assertThat(paramByPathCaptor.getValue().recursive()).isFalse(); } + @Test + public void getMultipleWithTrailingSlash() { + List 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); + + Map params = provider.getMultiple("/prod/app1/"); + assertThat(params).contains( + MapEntry.entry("key1", "foo1"), + MapEntry.entry("key2", "foo2"), + MapEntry.entry("key3", "foo3")); + assertThat(provider.get("/prod/app1/key1")).isEqualTo("foo1"); + assertThat(provider.get("/prod/app1/key2")).isEqualTo("foo2"); + assertThat(provider.get("/prod/app1/key3")).isEqualTo("foo3"); + + assertThat(paramByPathCaptor.getValue().path()).isEqualTo("/prod/app1"); + assertThat(paramByPathCaptor.getValue().withDecryption()).isFalse(); + assertThat(paramByPathCaptor.getValue().recursive()).isFalse(); + } + @Test public void getMultiple_cached_shouldNotCallSSM() { List parameters = new ArrayList<>(); From afd37e3c9217589af2350301285269d5274eb8a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Feb 2022 12:12:01 +0100 Subject: [PATCH 0049/1008] build(deps): bump aws.sdk.version from 2.17.131 to 2.17.132 (#760) Bumps `aws.sdk.version` from 2.17.131 to 2.17.132. Updates `software.amazon.awssdk:bom` from 2.17.131 to 2.17.132 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.131...2.17.132) Updates `http-client-spi` from 2.17.131 to 2.17.132 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.131...2.17.132) Updates `url-connection-client` from 2.17.131 to 2.17.132 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2eb5feba..50f935840 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.131 + 2.17.132 2.11.0 1.1.1 UTF-8 From a95445fc979bf61ba3525c9f4dba616421e887f3 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 18 Feb 2022 19:04:01 -0800 Subject: [PATCH 0050/1008] fix(docs): fix title for custom resources page (#763) --- docs/utilities/custom_resources.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/utilities/custom_resources.md b/docs/utilities/custom_resources.md index 3cf75800f..1f2acf36e 100644 --- a/docs/utilities/custom_resources.md +++ b/docs/utilities/custom_resources.md @@ -1,5 +1,6 @@ --- -title: Custom Resources description: Utility +title: Custom Resources +description: Utility --- [Custom resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) @@ -174,4 +175,4 @@ public class CustomSerializationHandler extends AbstractResourceHandler { .build(); } } -``` \ No newline at end of file +``` From d6fc0a97eab50f931314b5688457f20ee1a5b600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Feb 2022 12:11:39 +0100 Subject: [PATCH 0051/1008] build(deps): bump aws.sdk.version from 2.17.132 to 2.17.133 (#765) Bumps `aws.sdk.version` from 2.17.132 to 2.17.133. Updates `software.amazon.awssdk:bom` from 2.17.132 to 2.17.133 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.132...2.17.133) Updates `http-client-spi` from 2.17.132 to 2.17.133 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.132...2.17.133) Updates `url-connection-client` from 2.17.132 to 2.17.133 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50f935840..420d7cc2d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.132 + 2.17.133 2.11.0 1.1.1 UTF-8 From 76bf24b253ecc60f7edcd03ef6aece9e60ec4787 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Feb 2022 12:10:35 +0100 Subject: [PATCH 0052/1008] build(deps-dev): bump junit-pioneer from 1.5.0 to 1.6.1 (#768) Bumps [junit-pioneer](https://github.com/junit-pioneer/junit-pioneer) from 1.5.0 to 1.6.1. - [Release notes](https://github.com/junit-pioneer/junit-pioneer/releases) - [Commits](https://github.com/junit-pioneer/junit-pioneer/compare/v1.5.0...v1.6.1) --- updated-dependencies: - dependency-name: org.junit-pioneer:junit-pioneer dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index d067c9f96..16fe82fd7 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -91,7 +91,7 @@ org.junit-pioneer junit-pioneer - 1.5.0 + 1.6.1 test From 9bf3f750e312b065dd14ce9c6a4556372de523ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Feb 2022 12:11:13 +0100 Subject: [PATCH 0053/1008] build(deps): bump aws.sdk.version from 2.17.133 to 2.17.135 (#769) Bumps `aws.sdk.version` from 2.17.133 to 2.17.135. Updates `software.amazon.awssdk:bom` from 2.17.133 to 2.17.135 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.133...2.17.135) Updates `http-client-spi` from 2.17.133 to 2.17.135 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.133...2.17.135) Updates `url-connection-client` from 2.17.133 to 2.17.135 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 420d7cc2d..d0c7ae7aa 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.133 + 2.17.135 2.11.0 1.1.1 UTF-8 From fd0ef47a8fd14a349c41ddf713e8c0b9693d40a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Feb 2022 12:10:58 +0100 Subject: [PATCH 0054/1008] build(deps): bump aws.sdk.version from 2.17.135 to 2.17.136 (#770) Bumps `aws.sdk.version` from 2.17.135 to 2.17.136. Updates `software.amazon.awssdk:bom` from 2.17.135 to 2.17.136 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.135...2.17.136) Updates `http-client-spi` from 2.17.135 to 2.17.136 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.135...2.17.136) Updates `url-connection-client` from 2.17.135 to 2.17.136 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0c7ae7aa..af1a22c4c 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.135 + 2.17.136 2.11.0 1.1.1 UTF-8 From d2dc9532e0031aee405574845abe7aaff83ee695 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Feb 2022 12:12:03 +0100 Subject: [PATCH 0055/1008] build(deps): bump aws.sdk.version from 2.17.136 to 2.17.137 (#771) Bumps `aws.sdk.version` from 2.17.136 to 2.17.137. Updates `software.amazon.awssdk:bom` from 2.17.136 to 2.17.137 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.136...2.17.137) Updates `http-client-spi` from 2.17.136 to 2.17.137 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.136...2.17.137) Updates `url-connection-client` from 2.17.136 to 2.17.137 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af1a22c4c..7e4778479 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.136 + 2.17.137 2.11.0 1.1.1 UTF-8 From 894790b13085cda1b27a35325616324103592a30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:11:59 +0100 Subject: [PATCH 0056/1008] build(deps): bump aws.sdk.version from 2.17.137 to 2.17.138 (#773) Bumps `aws.sdk.version` from 2.17.137 to 2.17.138. Updates `software.amazon.awssdk:bom` from 2.17.137 to 2.17.138 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.137...2.17.138) Updates `http-client-spi` from 2.17.137 to 2.17.138 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.137...2.17.138) Updates `url-connection-client` from 2.17.137 to 2.17.138 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e4778479..384cf1fe8 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.1 2.13.1 1.9.7 - 2.17.137 + 2.17.138 2.11.0 1.1.1 UTF-8 From b456eaff7beabef9a23991ca895c2cd102cc6e94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:18:52 +0100 Subject: [PATCH 0057/1008] build(deps): bump log4j.version from 2.17.1 to 2.17.2 (#774) Bumps `log4j.version` from 2.17.1 to 2.17.2. Updates `log4j-core` from 2.17.1 to 2.17.2 Updates `log4j-slf4j-impl` from 2.17.1 to 2.17.2 Updates `log4j-api` from 2.17.1 to 2.17.2 Updates `log4j-layout-template-json` from 2.17.1 to 2.17.2 Updates `log4j-jcl` from 2.17.1 to 2.17.2 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 384cf1fe8..e2da132ea 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 1.8 1.8 - 2.17.1 + 2.17.2 2.13.1 1.9.7 2.17.138 From b95cf6444ff990e40c6506fea39574d8f17cddad Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Tue, 1 Mar 2022 09:54:53 +0100 Subject: [PATCH 0058/1008] chore: remove examples from the project as it was moved to aws-lambda-powertools-examples (#772) --- example/HelloWorldFunction/build.gradle | 25 --- example/HelloWorldFunction/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - example/HelloWorldFunction/gradlew | 185 ------------------ example/HelloWorldFunction/gradlew.bat | 104 ---------- example/HelloWorldFunction/pom.xml | 102 ---------- example/HelloWorldFunction/settings.gradle | 1 - .../src/main/java/helloworld/AppParams.java | 81 -------- .../main/java/helloworld/AppValidation.java | 51 ----- .../src/main/java/helloworld/MyObject.java | 34 ---- .../src/main/resources/log4j2.xml | 16 -- .../src/main/resources/schema.json | 55 ------ example/README.md | 121 ------------ example/events/event.json | 63 ------ example/events/eventSqs.json | 36 ---- example/template.yaml | 131 ------------- 17 files changed, 1011 deletions(-) delete mode 100644 example/HelloWorldFunction/build.gradle delete mode 100644 example/HelloWorldFunction/gradle.properties delete mode 100644 example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.jar delete mode 100644 example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties delete mode 100755 example/HelloWorldFunction/gradlew delete mode 100644 example/HelloWorldFunction/gradlew.bat delete mode 100644 example/HelloWorldFunction/pom.xml delete mode 100644 example/HelloWorldFunction/settings.gradle delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppParams.java delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/AppValidation.java delete mode 100644 example/HelloWorldFunction/src/main/java/helloworld/MyObject.java delete mode 100644 example/HelloWorldFunction/src/main/resources/log4j2.xml delete mode 100644 example/HelloWorldFunction/src/main/resources/schema.json delete mode 100644 example/README.md delete mode 100644 example/events/event.json delete mode 100644 example/events/eventSqs.json delete mode 100644 example/template.yaml diff --git a/example/HelloWorldFunction/build.gradle b/example/HelloWorldFunction/build.gradle deleted file mode 100644 index 18166b5f3..000000000 --- a/example/HelloWorldFunction/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -plugins{ - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - aspect 'software.amazon.lambda:powertools-parameters:1.11.0' - aspect 'software.amazon.lambda:powertools-validation:1.11.0' - - implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' - implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' - - implementation 'org.apache.logging.log4j:log4j-api:2.16.0' - implementation 'org.apache.logging.log4j:log4j-core:2.16.0' - - testImplementation 'junit:junit:4.13.2' -} - -sourceCompatibility = 11 -targetCompatibility = 11 diff --git a/example/HelloWorldFunction/gradle.properties b/example/HelloWorldFunction/gradle.properties deleted file mode 100644 index c84ec451a..000000000 --- a/example/HelloWorldFunction/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -aspectjVersion=1.9.6 \ No newline at end of file diff --git a/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.jar b/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 62d4c053550b91381bbd28b1afc82d634bf73a8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58910 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 diff --git a/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties b/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2e6e5897b..000000000 --- a/example/HelloWorldFunction/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/example/HelloWorldFunction/gradlew b/example/HelloWorldFunction/gradlew deleted file mode 100755 index 3a163de71..000000000 --- a/example/HelloWorldFunction/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or 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 UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# 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"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# 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 - ;; - 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" - which java >/dev/null 2>&1 || 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 - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" \ No newline at end of file diff --git a/example/HelloWorldFunction/gradlew.bat b/example/HelloWorldFunction/gradlew.bat deleted file mode 100644 index 9b30e1557..000000000 --- a/example/HelloWorldFunction/gradlew.bat +++ /dev/null @@ -1,104 +0,0 @@ -@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=. -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%" == "0" goto init - -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 init - -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 - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -: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 %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega \ No newline at end of file diff --git a/example/HelloWorldFunction/pom.xml b/example/HelloWorldFunction/pom.xml deleted file mode 100644 index 51a6a765d..000000000 --- a/example/HelloWorldFunction/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - 4.0.0 - helloworld - HelloWorld - 1.0 - jar - A sample Hello World created for SAM CLI. - - 11 - 11 - 2.17.1 - - - - - software.amazon.lambda - powertools-parameters - 1.11.0 - - - software.amazon.lambda - powertools-validation - 1.11.0 - - - com.amazonaws - aws-lambda-java-core - 1.2.1 - - - com.amazonaws - aws-lambda-java-events - 3.11.0 - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - com.fasterxml.jackson.core - jackson-databind - 2.11.2 - - - - junit - junit - 4.13.2 - test - - - - - - - org.codehaus.mojo - aspectj-maven-plugin - 1.14.0 - - ${maven.compiler.source} - ${maven.compiler.target} - ${maven.compiler.target} - - - software.amazon.lambda - powertools-validation - - - - - - - compile - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - - - package - - shade - - - - - - - diff --git a/example/HelloWorldFunction/settings.gradle b/example/HelloWorldFunction/settings.gradle deleted file mode 100644 index 94df0a3e5..000000000 --- a/example/HelloWorldFunction/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'HelloWorldFunction' diff --git a/example/HelloWorldFunction/src/main/java/helloworld/AppParams.java b/example/HelloWorldFunction/src/main/java/helloworld/AppParams.java deleted file mode 100644 index 5a2ea5d0f..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/AppParams.java +++ /dev/null @@ -1,81 +0,0 @@ -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 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 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 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; - -public class AppParams implements RequestHandler { - private final static Logger log = LogManager.getLogger(); - - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); - - String simplevalue = ssmProvider.defaultMaxAge(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 allvalues = ssmProvider.getMultiple("/powertools-java/sample"); - String b64value = ssmProvider.withTransformation(base64).get("/powertools-java/sample/keybase64"); - - Map 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 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()))) { - return br.lines().collect(Collectors.joining(System.lineSeparator())); - } - } -} diff --git a/example/HelloWorldFunction/src/main/java/helloworld/AppValidation.java b/example/HelloWorldFunction/src/main/java/helloworld/AppValidation.java deleted file mode 100644 index b66a0f864..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/AppValidation.java +++ /dev/null @@ -1,51 +0,0 @@ -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 software.amazon.lambda.powertools.validation.Validation; - -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; - -/** - * Handler for requests to Lambda function. - */ -public class AppValidation implements RequestHandler { - - @Validation(inboundSchema = "classpath:/schema.json") - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - Map 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()))) { - return br.lines().collect(Collectors.joining(System.lineSeparator())); - } - } -} diff --git a/example/HelloWorldFunction/src/main/java/helloworld/MyObject.java b/example/HelloWorldFunction/src/main/java/helloworld/MyObject.java deleted file mode 100644 index 3c416971e..000000000 --- a/example/HelloWorldFunction/src/main/java/helloworld/MyObject.java +++ /dev/null @@ -1,34 +0,0 @@ -package helloworld; - -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/example/HelloWorldFunction/src/main/resources/log4j2.xml b/example/HelloWorldFunction/src/main/resources/log4j2.xml deleted file mode 100644 index 033da8a11..000000000 --- a/example/HelloWorldFunction/src/main/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/example/HelloWorldFunction/src/main/resources/schema.json b/example/HelloWorldFunction/src/main/resources/schema.json deleted file mode 100644 index f38272f2d..000000000 --- a/example/HelloWorldFunction/src/main/resources/schema.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$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": [ - "id", - "name", - "price" - ], - "properties": { - "id": { - "$id": "#/properties/id", - "type": "integer", - "title": "Id of the product", - "description": "Unique identifier of the product", - "default": 0, - "examples": [ - 43242 - ] - }, - "name": { - "$id": "#/properties/name", - "type": "string", - "title": "Name of the product", - "description": "Explicit name of the product", - "minLength": 5, - "default": "", - "examples": [ - "FooBar XY" - ] - }, - "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/example/README.md b/example/README.md deleted file mode 100644 index 56af61c73..000000000 --- a/example/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# example - -This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. - -- HelloWorldFunction/src/main - Code for the application's Lambda function. -- events - Invocation events that you can use to invoke the function. -- HelloWorldFunction/src/test - Unit tests for the application code. -- template.yaml - A template that defines the application's AWS resources. - -The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. - -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) - -## Deploy the sample application - -The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. - -To use the SAM CLI, you need the following tools. - -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java8 - [Install the Java SE Development Kit 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) -* Maven - [Install Maven](https://maven.apache.org/install.html) or -* Gradle - [Install Gradle](https://gradle.org/install/) -* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) - -To build and deploy your application for the first time, run the following in your shell: - -```bash -sam build -sam deploy --guided -``` - -The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: - -* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. -* **AWS Region**: The AWS region you want to deploy your app to. -* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. -* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modified IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. -* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. - -You can find your API Gateway Endpoint URL in the output values displayed after deployment. - -## Use the SAM CLI to build and test locally - -Build your application with the `sam build` command. - -```bash -example$ sam build -``` - -The SAM CLI installs dependencies defined in `HelloWorldFunction/build.gradle`, creates a deployment package, and saves it in the `.aws-sam/build` folder. - -Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. - -Run functions locally and invoke them with the `sam local invoke` command. - -```bash -example$ sam local invoke HelloWorldFunction --event events/event.json -``` - -The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. - -```bash -example$ sam local start-api -example$ curl http://localhost:3000/ -``` - -The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. - -```yaml - Events: - HelloWorld: - Type: Api - Properties: - Path: /hello - Method: get -``` - -## Add a resource to your application -The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. - -## Fetch, tail, and filter Lambda function logs - -To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. - -`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. - -```bash -example$ sam logs -n HelloWorldFunction --stack-name sam-app --tail -``` - -You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). - -## Unit tests - -Tests are defined in the `HelloWorldFunction/src/test` folder in this project. - -```bash -example$ cd HelloWorldFunction -HelloWorldFunction$ gradle test -``` - -## Cleanup - -To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: - -```bash -aws cloudformation delete-stack --stack-name example -``` - -## Resources - -See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. - -Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/example/events/event.json b/example/events/event.json deleted file mode 100644 index 3822fadaa..000000000 --- a/example/events/event.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "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" - } - } - \ No newline at end of file diff --git a/example/events/eventSqs.json b/example/events/eventSqs.json deleted file mode 100644 index 37a29c4dd..000000000 --- a/example/events/eventSqs.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "Records": [ - { - "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb99", - "receiptHandle": "MessageReceiptHandle", - "body": "Hello from SQS!", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1523232000000", - "SenderId": "123456789012", - "ApproximateFirstReceiveTimestamp": "1523232000001" - }, - "messageAttributes": {}, - "md5OfBody": "7b270e59b47ff90a553787216d55d999", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:eu-west-1:123456789:powertools-example-TestSqsQueue-1JW5W8N9", - "awsRegion": "eu-west-1" - }, - { - "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", - "receiptHandle": "MessageReceiptHandle", - "body": "Hello from SQS!", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1523232000000", - "SenderId": "123456789012", - "ApproximateFirstReceiveTimestamp": "1523232000001" - }, - "messageAttributes": {}, - "md5OfBody": "7b270e59b47ff90a553787216d55d91d", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:eu-west-1:123456789:powertools-example-TestSqsQueue-1JW5W8N9", - "awsRegion": "eu-west-1" - } - ] -} \ No newline at end of file diff --git a/example/template.yaml b/example/template.yaml deleted file mode 100644 index 907ae13b1..000000000 --- a/example/template.yaml +++ /dev/null @@ -1,131 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - sam-app - - Sample SAM Template for sam-app - -# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst -Globals: - Function: - Timeout: 20 - Runtime: java11 - -Resources: - HelloWorldValidationFunction: - 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: HelloWorldFunction - Handler: helloworld.AppValidation::handleRequest - MemorySize: 512 - Tracing: Active - 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: post - - HelloWorldParamsFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: HelloWorldFunction - Handler: helloworld.AppParams::handleRequest - MemorySize: 512 - Tracing: Active - 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: /helloparams - Method: get - - IdempotencyTable: - Type: AWS::Serverless::SimpleTable - Properties: - TableName: idempotency_table - PrimaryKey: - Name: id - Type: String - - 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: - # 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/" - - HelloWorldStreamApi: - Description: "API Gateway endpoint URL for Prod stage for Hello World stream function" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hellostream/" - - HelloWorldParamsApi: - Description: "API Gateway endpoint URL for Prod stage for Hello World params function" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/helloparams/" - HelloWorldParamsFunction: - Description: "Hello World Params Lambda Function ARN" - Value: !GetAtt HelloWorldParamsFunction.Arn \ No newline at end of file From bcf9ababdfe64fda62ff023ee36a18825f976165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Tue, 1 Mar 2022 10:22:32 +0100 Subject: [PATCH 0059/1008] feat: Easy Event Deserialization (#757) * use serialization lib instead of internal stuff * add ActiveMq & RabbitMQ * feature: easy deserialization of event content * rename methods in deserializer * Update powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java Co-authored-by: Pankaj Agrawal * chore: remove SQS and Idempotency examples (#754) * build(deps): bump maven-jar-plugin from 3.2.0 to 3.2.2 (#756) Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.0 to 3.2.2. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.0...maven-jar-plugin-3.2.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.17.130 to 2.17.131 (#755) Bumps `aws.sdk.version` from 2.17.130 to 2.17.131. Updates `software.amazon.awssdk:bom` from 2.17.130 to 2.17.131 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.130...2.17.131) Updates `http-client-spi` from 2.17.130 to 2.17.131 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.130...2.17.131) Updates `url-connection-client` from 2.17.130 to 2.17.131 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix #758 (#759) remove trailing slash for multiple params * changes post review: - javadoc - indentation - better edge cases handling * add tests for edge cases * exception messages detailed * deserialize a string as list * documentation * gradle example Co-authored-by: Pankaj Agrawal Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/utilities/serialization.md | 244 +++++++++++++++++- pom.xml | 6 + powertools-serialization/pom.xml | 13 + .../EventDeserializationException.java | 26 ++ .../utilities/EventDeserializer.java | 231 +++++++++++++++++ .../powertools/utilities/JsonConfig.java | 8 +- .../utilities/EventDeserializerTest.java | 172 ++++++++++++ .../powertools/utilities/model/Basket.java | 55 ++++ .../powertools/utilities/model/Product.java | 70 +++++ .../src/test/resources/apigw_event.json | 62 +++++ .../test/resources/apigw_event_no_body.json | 61 +++++ .../src/test/resources/kafka_event.json | 27 ++ .../src/test/resources/kinesis_event.json | 38 +++ .../src/test/resources/sns_event.json | 27 ++ .../src/test/resources/sqs_event.json | 40 +++ .../src/test/resources/sqs_event_no_body.json | 21 ++ powertools-validation/pom.xml | 5 + .../validation/internal/ValidationAspect.java | 10 +- .../internal/ValidationAspectTest.java | 14 +- .../src/test/resources/kinesis.json | 2 +- .../src/test/resources/sqs.json | 2 +- spotbugs-exclude.xml | 8 + 22 files changed, 1125 insertions(+), 17 deletions(-) create mode 100644 powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java create mode 100644 powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java create mode 100644 powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java create mode 100644 powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java create mode 100644 powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java create mode 100644 powertools-serialization/src/test/resources/apigw_event.json create mode 100644 powertools-serialization/src/test/resources/apigw_event_no_body.json create mode 100644 powertools-serialization/src/test/resources/kafka_event.json create mode 100644 powertools-serialization/src/test/resources/kinesis_event.json create mode 100644 powertools-serialization/src/test/resources/sns_event.json create mode 100644 powertools-serialization/src/test/resources/sqs_event.json create mode 100644 powertools-serialization/src/test/resources/sqs_event_no_body.json diff --git a/docs/utilities/serialization.md b/docs/utilities/serialization.md index 19ff00d37..d9e392ca4 100644 --- a/docs/utilities/serialization.md +++ b/docs/utilities/serialization.md @@ -3,7 +3,249 @@ title: Serialization Utilities description: Utility --- -This module contains a set of utilities you may use in your Lambda functions, mainly associated with other modules like [validation](validation.md) and [idempotency](idempotency.md), to manipulate JSON. +This module contains a set of utilities you may use in your Lambda functions, to manipulate JSON. + +## Easy deserialization + +### Key features + +* Easily deserialize the main content of an event (for example, the body of an API Gateway event) +* 15+ built-in events (see the [list below](#built-in-events)) + +### Getting started + +=== "Maven" + + ```xml hl_lines="5" + + ... + + software.amazon.lambda + powertools-serialization + {{ powertools.version }} + + ... + + ``` + +=== "Gradle" + + ``` + implementation 'software.amazon.lambda:powertools-serialization:{{ powertools.version }}' + ``` + +### EventDeserializer + +The `EventDeserializer` can be used to extract the main part of an event (body, message, records) and deserialize it from JSON to your desired type. + +It can handle single elements like the body of an API Gateway event: + +=== "APIGWHandler.java" + + ```java hl_lines="1 6 9" + import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + + public class APIGWHandler implements RequestHandler { + + public APIGatewayProxyResponseEvent handleRequest( + final APIGatewayProxyRequestEvent event, + final Context context) { + + Product product = extractDataFrom(event).as(Product.class); + + } + ``` + +=== "Product.java" + + ```java + 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; + } + } + ``` + +=== "event" + + ```json hl_lines="2" + { + "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", + "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" + } + } + ``` + +It can also handle a collection of elements like the records of an SQS event: + +=== "SQSHandler.java" + + ```java hl_lines="1 6 9" + import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + + public class SQSHandler implements RequestHandler { + + public String handleRequest( + final SQSEvent event, + final Context context) { + + List products = extractDataFrom(event).asListOf(Product.class); + + } + ``` + +=== "event" + + ```json hl_lines="6 23" + { + "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" + } + ] + } + ``` + +!!! Tip + In the background, `EventDeserializer` is using Jackson. The `ObjectMapper` is configured in `JsonConfig`. You can customize the configuration of the mapper if needed: + `JsonConfig.get().getObjectMapper()`. Using this feature, you don't need to add Jackson to your project and create another instance of `ObjectMapper`. + +### Built-in events + +| Event Type | Path to the content | List | +|---------------------------------------------------|-----------------------------------------------------------|------| +| `APIGatewayProxyRequestEvent` | `body` | | +| `APIGatewayV2HTTPEvent` | `body` | | +| `SNSEvent` | `Records[0].Sns.Message` | | +| `SQSEvent` | `Records[*].body` | x | + | `ScheduledEvent` | `detail` | | + | `ApplicationLoadBalancerRequestEvent` | `body` | | + | `CloudWatchLogsEvent` | `powertools_base64_gzip(data)` | | + | `CloudFormationCustomResourceEvent` | `resourceProperties` | | + | `KinesisEvent` | `Records[*].kinesis.powertools_base64(data)` | x | + | `KinesisFirehoseEvent` | `Records[*].powertools_base64(data)` | x | + | `KafkaEvent` | `records[*].values[*].powertools_base64(value)` | x | + | `ActiveMQEvent` | `messages[*].powertools_base64(data)` | x | +| `RabbitMQEvent` | `rmqMessagesByQueue[*].values[*].powertools_base64(data)` | x | +| `KinesisAnalyticsFirehoseInputPreprocessingEvent` | `Records[*].kinesis.powertools_base64(data)` | x | +| `KinesisAnalyticsStreamsInputPreprocessingEvent` | `Records[*].kinesis.powertools_base64(data)` | x | + ## JMESPath functions diff --git a/pom.xml b/pom.xml index e2da132ea..cdfa8dd2c 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ UTF-8 1.2.1 3.11.0 + 1.0.0 3.10.0 1.14.0 2.22.2 @@ -122,6 +123,11 @@ aws-lambda-java-events ${lambda.events.version} + + com.amazonaws + aws-lambda-java-serialization + ${lambda.serial.version} + software.amazon.awssdk bom diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 5cff32313..e460b7022 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -45,6 +45,14 @@ io.burt jmespath-jackson + + com.amazonaws + aws-lambda-java-events + + + org.apache.logging.log4j + log4j-slf4j-impl + @@ -57,6 +65,11 @@ assertj-core test + + com.amazonaws + aws-lambda-java-tests + test + diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java new file mode 100644 index 000000000..2b06c9256 --- /dev/null +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities; + +public class EventDeserializationException extends RuntimeException { + private static final long serialVersionUID = -5003158148870110442L; + + public EventDeserializationException(String msg, Exception e) { + super(msg, e); + } + + public EventDeserializationException(String msg) { + super(msg); + } +} diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java new file mode 100644 index 000000000..9742299ee --- /dev/null +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java @@ -0,0 +1,231 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities; + +import com.amazonaws.services.lambda.runtime.events.*; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; + +/** + * Class that can be used to extract the meaningful part of an event and deserialize it into a Java object.
+ * For example, extract the body of an API Gateway event, or messages from an SQS event. + */ +public class EventDeserializer { + + private static final Logger LOG = LoggerFactory.getLogger(EventDeserializer.class); + + /** + * Extract the meaningful part of a Lambda Event object. Main events are built-in: + *
    + *
  • {@link APIGatewayProxyRequestEvent} -> body
  • + *
  • {@link APIGatewayV2HTTPEvent} -> body
  • + *
  • {@link SNSEvent} -> Records[0].Sns.Message
  • + *
  • {@link SQSEvent} -> Records[*].body (list)
  • + *
  • {@link ScheduledEvent} -> detail
  • + *
  • {@link ApplicationLoadBalancerRequestEvent} -> body
  • + *
  • {@link CloudWatchLogsEvent} -> powertools_base64_gzip(data)
  • + *
  • {@link CloudFormationCustomResourceEvent} -> resourceProperties
  • + *
  • {@link KinesisEvent} -> Records[*].kinesis.powertools_base64(data) (list)
  • + *
  • {@link KinesisFirehoseEvent} -> Records[*].powertools_base64(data) (list)
  • + *
  • {@link KafkaEvent} -> records[*].values[*].powertools_base64(value) (list)
  • + *
  • {@link ActiveMQEvent} -> messages[*].powertools_base64(data) (list)
  • + *
  • {@link RabbitMQEvent} -> rmqMessagesByQueue[*].values[*].powertools_base64(data) (list)
  • + *
  • {@link KinesisAnalyticsFirehoseInputPreprocessingEvent} -> Records[*].kinesis.powertools_base64(data) (list)
  • + *
  • {@link KinesisAnalyticsStreamsInputPreprocessingEvent} > Records[*].kinesis.powertools_base64(data) (list)
  • + *
  • {@link String}
  • + *
  • {@link Map}
  • + *
+ * To be used in conjunction with {@link EventPart#as(Class)} or {@link EventPart#asListOf(Class)} + * for the deserialization. + * + * @param object the event of your Lambda function handler method + * @return the part of the event which is meaningful (ex: body of the API Gateway).
+ */ + public static EventPart extractDataFrom(Object object) { + if (object instanceof String) { + return new EventPart((String) object); + } else if (object instanceof Map) { + return new EventPart((Map) object); + } else if (object instanceof APIGatewayProxyRequestEvent) { + APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) object; + return new EventPart(event.getBody()); + } else if (object instanceof APIGatewayV2HTTPEvent) { + APIGatewayV2HTTPEvent event = (APIGatewayV2HTTPEvent) object; + return new EventPart(event.getBody()); + } else if (object instanceof SNSEvent) { + SNSEvent event = (SNSEvent) object; + return new EventPart(event.getRecords().get(0).getSNS().getMessage()); + } else if (object instanceof SQSEvent) { + SQSEvent event = (SQSEvent) object; + return new EventPart(event.getRecords().stream() + .map(SQSEvent.SQSMessage::getBody) + .collect(Collectors.toList())); + } else if (object instanceof ScheduledEvent) { + ScheduledEvent event = (ScheduledEvent) object; + return new EventPart(event.getDetail()); + } else if (object instanceof ApplicationLoadBalancerRequestEvent) { + ApplicationLoadBalancerRequestEvent event = (ApplicationLoadBalancerRequestEvent) object; + return new EventPart(event.getBody()); + } else if (object instanceof CloudWatchLogsEvent) { + CloudWatchLogsEvent event = (CloudWatchLogsEvent) object; + return new EventPart(decompress(decode(event.getAwsLogs().getData().getBytes(UTF_8)))); + } else if (object instanceof CloudFormationCustomResourceEvent) { + CloudFormationCustomResourceEvent event = (CloudFormationCustomResourceEvent) object; + return new EventPart(event.getResourceProperties()); + } else if (object instanceof KinesisEvent) { + KinesisEvent event = (KinesisEvent) object; + return new EventPart(event.getRecords().stream() + .map(r -> decode(r.getKinesis().getData())) + .collect(Collectors.toList())); + } else if (object instanceof KinesisFirehoseEvent) { + KinesisFirehoseEvent event = (KinesisFirehoseEvent) object; + return new EventPart(event.getRecords().stream() + .map(r -> decode(r.getData())) + .collect(Collectors.toList())); + } else if (object instanceof KafkaEvent) { + KafkaEvent event = (KafkaEvent) object; + return new EventPart(event.getRecords().values().stream() + .flatMap(List::stream) + .map(r -> decode(r.getValue())) + .collect(Collectors.toList())); + } else if (object instanceof ActiveMQEvent) { + ActiveMQEvent event = (ActiveMQEvent) object; + return new EventPart(event.getMessages().stream() + .map(m -> decode(m.getData())) + .collect(Collectors.toList())); + } else if (object instanceof RabbitMQEvent) { + RabbitMQEvent event = (RabbitMQEvent) object; + return new EventPart(event.getRmqMessagesByQueue().values().stream() + .flatMap(List::stream) + .map(r -> decode(r.getData())) + .collect(Collectors.toList())); + } else if (object instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { + KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) object; + return new EventPart(event.getRecords().stream() + .map(r -> decode(r.getData())) + .collect(Collectors.toList())); + } else if (object instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { + KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) object; + return new EventPart(event.getRecords().stream() + .map(r -> decode(r.getData())) + .collect(Collectors.toList())); + } else { + // does not really make sense to use this EventDeserializer when you already have a typed object + // just not to throw an exception + LOG.warn("Consider using your object directly instead of using EventDeserializer"); + return new EventPart(object); + } + } + + /** + * Meaningful part of a Lambda event.
+ * Use {@link #extractDataFrom(Object)} to retrieve an instance of this class. + */ + public static class EventPart { + private Map contentMap; + private String content; + private List contentList; + private Object contentObject; + + private EventPart(List contentList) { + this.contentList = contentList; + } + + private EventPart(String content) { + this.content = content; + } + + private EventPart(Map contentMap) { + this.contentMap = contentMap; + } + + private EventPart(Object content) { + this.contentObject = content; + } + + /** + * Deserialize this part of event from JSON to an object of type T + * @param clazz the target type for deserialization + * @param type of object to return + * @return an Object of type T (deserialized from the content) + */ + public T as(Class clazz) { + try { + if (content != null) { + if (content.getClass().equals(clazz)) { + // do not read json when returning String, just return the String + return (T) content; + } + return JsonConfig.get().getObjectMapper().reader().readValue(content, clazz); + } + if (contentMap != null) { + return JsonConfig.get().getObjectMapper().convertValue(contentMap, clazz); + } + if (contentObject != null) { + return (T) contentObject; + } + if (contentList != null) { + throw new EventDeserializationException("The content of this event is a list, consider using 'asListOf' instead"); + } + // should not occur, except if the event is malformed (missing fields) + throw new IllegalStateException("Event content is null: the event may be malformed (missing fields)"); + } catch (IOException e) { + throw new EventDeserializationException("Cannot load the event as " + clazz.getSimpleName(), e); + } + } + + /** + * Deserialize this part of event from JSON to a list of objects of type T + * @param clazz the target type for deserialization + * @param type of object to return + * @return a list of objects of type T (deserialized from the content) + */ + public List asListOf(Class clazz) { + if (contentList == null && content == null) { + if (contentMap != null || contentObject != null) { + throw new EventDeserializationException("The content of this event is not a list, consider using 'as' instead"); + } + // should not occur, except if the event is really malformed + throw new IllegalStateException("Event content is null: the event may be malformed (missing fields)"); + } + if (content != null) { + ObjectReader reader = JsonConfig.get().getObjectMapper().readerForListOf(clazz); + try { + return reader.readValue(content); + } catch (JsonProcessingException e) { + throw new EventDeserializationException("Cannot load the event as a list of " + clazz.getSimpleName() + ", consider using 'as' instead", e); + } + } else { + return contentList.stream().map(s -> { + try { + return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); + } catch (IOException e) { + throw new EventDeserializationException("Cannot load the event as a list of " + clazz.getSimpleName(), e); + } + }).collect(Collectors.toList()); + } + } + } +} 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 c3a5fc865..d8af1c0cb 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 @@ -24,8 +24,6 @@ import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; -import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; - public class JsonConfig { private JsonConfig() { } @@ -38,11 +36,7 @@ public static JsonConfig get() { return ConfigHolder.instance; } - private static final ThreadLocal om = ThreadLocal.withInitial(() -> { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); - return objectMapper; - }); + private static final ThreadLocal om = ThreadLocal.withInitial(ObjectMapper::new); private final FunctionRegistry defaultFunctions = FunctionRegistry.defaultRegistry(); private final FunctionRegistry customFunctions = defaultFunctions.extend( 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 new file mode 100644 index 000000000..90143b2a0 --- /dev/null +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java @@ -0,0 +1,172 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities; + +import com.amazonaws.services.lambda.runtime.events.*; +import com.amazonaws.services.lambda.runtime.tests.annotations.Event; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import software.amazon.lambda.powertools.utilities.model.Basket; +import software.amazon.lambda.powertools.utilities.model.Product; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + +public class EventDeserializerTest { + + @Test + public void testDeserializeStringAsString_shouldReturnString() { + String stringEvent = "Hello World"; + String result = extractDataFrom(stringEvent).as(String.class); + assertThat(result).isEqualTo(stringEvent); + } + + @Test + public 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}]"; + List products = extractDataFrom(productStr).asListOf(Product.class); + assertThat(products).hasSize(2); + assertProduct(products.get(0)); + } + + @Test + public void testDeserializeStringAsList_shouldThrowException() { + String productStr = "{\"id\":1234, \"name\":\"product\", \"price\":42}"; + assertThatThrownBy(() -> extractDataFrom(productStr).asListOf(Product.class)) + .isInstanceOf(EventDeserializationException.class) + .hasMessage("Cannot load the event as a list of Product, consider using 'as' instead"); + } + + @Test + public void testDeserializeMapAsObject_shouldReturnObject() { + Map map = new HashMap<>(); + map.put("id", 1234); + map.put("name", "product"); + map.put("price", 42); + Product product = extractDataFrom(map).as(Product.class); + assertProduct(product); + } + + @ParameterizedTest + @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) + public 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) { + Product product = extractDataFrom(event).as(Product.class); + assertProduct(product); + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event) { + List products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(2); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + public void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEvent event) { + List products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(2); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "kafka_event.json", type = KafkaEvent.class) + public void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent event) { + List products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(2); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent event) { + assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) + .isInstanceOf(EventDeserializationException.class) + .hasMessageContaining("consider using 'asListOf' instead"); + } + + @ParameterizedTest + @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) + public void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGatewayProxyRequestEvent event) { + assertThatThrownBy(() -> extractDataFrom(event).asListOf(Product.class)) + .isInstanceOf(EventDeserializationException.class) + .hasMessageContaining("consider using 'as' instead"); + } + + @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) { + assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Event content is null"); + } + + @ParameterizedTest + @Event(value = "sqs_event_no_body.json", type = SQSEvent.class) + public void testDeserializeSQSEventNoBody_shouldThrowException(SQSEvent event) { + List products = extractDataFrom(event).asListOf(Product.class); + assertThat(products.get(0)).isNull(); + } + + @Test + public 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)) + .usingRecursiveComparison(); + } + +} diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java new file mode 100644 index 000000000..228089c52 --- /dev/null +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class Basket { + private List products = new ArrayList<>(); + + public List getProducts() { + return products; + } + + public void setProducts(List products) { + this.products = products; + } + + public Basket() { + } + + public Basket( Product ...p){ + products.addAll(Arrays.asList(p)); + } + + 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); + } +} diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java new file mode 100644 index 000000000..f03f6d426 --- /dev/null +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.utilities.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); + } +} diff --git a/powertools-serialization/src/test/resources/apigw_event.json b/powertools-serialization/src/test/resources/apigw_event.json new file mode 100644 index 000000000..7758cb0bb --- /dev/null +++ b/powertools-serialization/src/test/resources/apigw_event.json @@ -0,0 +1,62 @@ +{ + "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" + } +} diff --git a/powertools-serialization/src/test/resources/apigw_event_no_body.json b/powertools-serialization/src/test/resources/apigw_event_no_body.json new file mode 100644 index 000000000..f534c91a3 --- /dev/null +++ b/powertools-serialization/src/test/resources/apigw_event_no_body.json @@ -0,0 +1,61 @@ +{ + "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/powertools-serialization/src/test/resources/kafka_event.json b/powertools-serialization/src/test/resources/kafka_event.json new file mode 100644 index 000000000..cf1bad615 --- /dev/null +++ b/powertools-serialization/src/test/resources/kafka_event.json @@ -0,0 +1,27 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-3432434/4834-3547-3455-9872-7929", + "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-01": [ + { + "topic": "mytopic1", + "partition": 0, + "offset": 15, + "timestamp": 1596480920837, + "timestampType": "CREATE_TIME", + "value": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=" + } + ], + "mytopic-02": [ + { + "topic": "mytopic2", + "partition": 0, + "offset": 15, + "timestamp": 1596480920838, + "timestampType": "CREATE_TIME", + "value": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==" + } + ] + } +} diff --git a/powertools-serialization/src/test/resources/kinesis_event.json b/powertools-serialization/src/test/resources/kinesis_event.json new file mode 100644 index 000000000..5b95ddaf4 --- /dev/null +++ b/powertools-serialization/src/test/resources/kinesis_event.json @@ -0,0 +1,38 @@ +{ + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "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": "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" + } + ] +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/sns_event.json b/powertools-serialization/src/test/resources/sns_event.json new file mode 100644 index 000000000..317a657d9 --- /dev/null +++ b/powertools-serialization/src/test/resources/sns_event.json @@ -0,0 +1,27 @@ +{ + "Records": [ + { + "EventSource": "aws:sns", + "EventVersion": "1.0", + "EventSubscriptionArn": "arn:aws:sns:eu-central-1:123456789012:TopicSendToMe:e3ddc7d5-2f86-40b8-a13d-3362f94fd8dd", + "Sns": { + "Type": "Notification", + "MessageId": "dc918f50-80c6-56a2-ba33-d8a9bbf013ab", + "TopicArn": "arn:aws:sns:eu-central-1:123456789012:TopicSendToMe", + "Subject": "Test sns message", + "Message": "{\"id\":1234, \"name\":\"product\", \"price\":42}", + "Timestamp": "2020-10-08T16:06:14.656Z", + "SignatureVersion": "1", + "Signature": "UWnPpkqPAphyr+6PXzUF9++4zJcw==", + "SigningCertUrl": "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-a86cb10b4e1f29c941702d737128f7b6.pem", + "UnsubscribeUrl": "https://sns.eu-central-1.amazonaws.com/?Action=Unsubscribe", + "MessageAttributes": { + "name": { + "Type": "String", + "Value": "Bob" + } + } + } + } + ] +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/sqs_event.json b/powertools-serialization/src/test/resources/sqs_event.json new file mode 100644 index 000000000..d33db4b53 --- /dev/null +++ b/powertools-serialization/src/test/resources/sqs_event.json @@ -0,0 +1,40 @@ +{ + "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": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 12345,\n \"name\": \"product5\",\n \"price\": 45\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-serialization/src/test/resources/sqs_event_no_body.json b/powertools-serialization/src/test/resources/sqs_event_no_body.json new file mode 100644 index 000000000..3a313dd6b --- /dev/null +++ b/powertools-serialization/src/test/resources/sqs_event_no_body.json @@ -0,0 +1,21 @@ +{ + "Records": [ + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "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-validation/pom.xml b/powertools-validation/pom.xml index db73fb12d..721f30a28 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -87,6 +87,11 @@ junit-jupiter-engine test + + com.amazonaws + aws-lambda-java-serialization + test + org.apache.commons commons-lang3 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 b42ce71ab..0a1f00599 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 @@ -91,8 +91,14 @@ && placedOnRequestHandler(pjp)) { event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof KafkaEvent) { KafkaEvent event = (KafkaEvent) obj; - event.getRecords().forEach((s, records) -> records.forEach(record -> validate(record.getValue(), inboundJsonSchema))); - }else if (obj instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { + event.getRecords().forEach((s, records) -> records.forEach(record -> validate(decode(record.getValue()), inboundJsonSchema))); + } else if (obj instanceof ActiveMQEvent) { + ActiveMQEvent event = (ActiveMQEvent) obj; + event.getMessages().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); + } else if (obj instanceof RabbitMQEvent) { + RabbitMQEvent event = (RabbitMQEvent) obj; + event.getRmqMessagesByQueue().forEach((s, records) -> records.forEach(record -> validate(decode(record.getData()), inboundJsonSchema))); + } else if (obj instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { 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 2bbe7cdaa..c8741f1e3 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 @@ -18,12 +18,14 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.KinesisEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; +import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; 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; import software.amazon.lambda.powertools.validation.ValidationConfig; +import software.amazon.lambda.powertools.validation.ValidationException; import software.amazon.lambda.powertools.validation.handlers.*; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; @@ -91,16 +93,18 @@ public void validate_inputKO_schemaInString_shouldThrowValidationException() { } @Test - public void validate_SQS() throws IOException { - SQSEvent event = ValidationConfig.get().getObjectMapper().readValue(this.getClass().getResourceAsStream("/sqs.json"), SQSEvent.class); + public void validate_SQS() { + PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); SQSHandler handler = new SQSHandler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } @Test - public void validate_Kinesis() throws IOException { - KinesisEvent event = ValidationConfig.get().getObjectMapper().readValue(this.getClass().getResourceAsStream("/kinesis.json"), KinesisEvent.class); + public void validate_Kinesis() { + PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); + KinesisEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/kinesis.json")); KinesisHandler handler = new KinesisHandler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); diff --git a/powertools-validation/src/test/resources/kinesis.json b/powertools-validation/src/test/resources/kinesis.json index 6d99be7e5..ad33ae456 100644 --- a/powertools-validation/src/test/resources/kinesis.json +++ b/powertools-validation/src/test/resources/kinesis.json @@ -1,5 +1,5 @@ { - "records": [ + "Records": [ { "kinesis": { "partitionKey": "partitionKey-03", diff --git a/powertools-validation/src/test/resources/sqs.json b/powertools-validation/src/test/resources/sqs.json index 9180c5839..129e79bb2 100644 --- a/powertools-validation/src/test/resources/sqs.json +++ b/powertools-validation/src/test/resources/sqs.json @@ -1,5 +1,5 @@ { - "records": [ + "Records": [ { "messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db2", "receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==", diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 30e627a56..0c8d1d8f8 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -77,6 +77,14 @@ + + + + + + + + From 8ac0783990f97b9edab475c5bd8c9b5d38ae2533 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Tue, 1 Mar 2022 11:35:03 +0100 Subject: [PATCH 0060/1008] chore:Prep release 1.12.0 (#775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore:prep release 1.12.0 * Update CHANGELOG.md Co-authored-by: Jérôme Van Der Linden Co-authored-by: pankajagrawal16 Co-authored-by: Jérôme Van Der Linden --- CHANGELOG.md | 9 +++++++++ README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1112f04da..887cd6287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,15 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.12.0] - 2022-03-01 + +### Added + * **Easy Event Deserialization**: Extraction and deserialization of the main content of events (body, messages, ...) [#757](https://github.com/awslabs/aws-lambda-powertools-java/pull/757) + +### Bug Fixes + * Different behavior while using SSMProvider with or without trailing slash in parameter names [#758](https://github.com/awslabs/aws-lambda-powertools-java/issues/758) + + ## [1.11.0] - 2022-02-16 ### Added diff --git a/README.md b/README.md index 09c0f52be..a8a03350e 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.11.0 + 1.12.0 software.amazon.lambda powertools-logging - 1.11.0 + 1.12.0 software.amazon.lambda powertools-metrics - 1.11.0 + 1.12.0 ... diff --git a/mkdocs.yml b/mkdocs.yml index 59b02d22c..151d1f779 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,7 +77,7 @@ extra_javascript: extra: powertools: - version: 1.11.0 + version: 1.12.0 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index cdfa8dd2c..e19580615 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.11.0 + 1.12.0 pom AWS Lambda Powertools Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 7414e4576..e619cd7fc 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 49bd90f31..a65c52311 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 16fe82fd7..9918753da 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.11.0 + 1.12.0 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 1750993eb..caf4e7073 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 604b69e45..4c6c10d23 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 9a18a6788..e2b7245ae 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e460b7022..214f4575e 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 7b187f0c3..b701959ff 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index ce6677e7b..518b7e995 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 5d5cb1e2c..13d23b2fd 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 721f30a28..8c548cef8 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.11.0 + 1.12.0 AWS Lambda Powertools Java validation library From bd3f3e5de3953d7f02499a31b47f77c93d1e48e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 12:23:36 +0100 Subject: [PATCH 0061/1008] build(deps): bump aws.sdk.version from 2.17.138 to 2.17.139 (#776) Bumps `aws.sdk.version` from 2.17.138 to 2.17.139. Updates `software.amazon.awssdk:bom` from 2.17.138 to 2.17.139 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.138...2.17.139) Updates `http-client-spi` from 2.17.138 to 2.17.139 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.138...2.17.139) Updates `url-connection-client` from 2.17.138 to 2.17.139 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e19580615..48f008e1f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.138 + 2.17.139 2.11.0 1.1.1 UTF-8 From 91c568d65785a407697861a5943da7b3c094369e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 12:11:38 +0100 Subject: [PATCH 0062/1008] build(deps): bump aws.sdk.version from 2.17.139 to 2.17.140 (#777) Bumps `aws.sdk.version` from 2.17.139 to 2.17.140. Updates `software.amazon.awssdk:bom` from 2.17.139 to 2.17.140 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.139...2.17.140) Updates `http-client-spi` from 2.17.139 to 2.17.140 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.139...2.17.140) Updates `url-connection-client` from 2.17.139 to 2.17.140 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 48f008e1f..73b8c1af0 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.139 + 2.17.140 2.11.0 1.1.1 UTF-8 From 48fcbade617d25892131cdb7b321813267ec6fa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Mar 2022 12:12:46 +0100 Subject: [PATCH 0063/1008] build(deps): bump aws.sdk.version from 2.17.140 to 2.17.141 (#778) Bumps `aws.sdk.version` from 2.17.140 to 2.17.141. Updates `software.amazon.awssdk:bom` from 2.17.140 to 2.17.141 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.140...2.17.141) Updates `http-client-spi` from 2.17.140 to 2.17.141 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.140...2.17.141) Updates `url-connection-client` from 2.17.140 to 2.17.141 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73b8c1af0..4f9a24ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.140 + 2.17.141 2.11.0 1.1.1 UTF-8 From 2529aaa3f5e2484c9b8aceb6d67bcdcccc4a3eee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Mar 2022 12:10:27 +0100 Subject: [PATCH 0064/1008] build(deps): bump aws.sdk.version from 2.17.141 to 2.17.142 (#780) Bumps `aws.sdk.version` from 2.17.141 to 2.17.142. Updates `software.amazon.awssdk:bom` from 2.17.141 to 2.17.142 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.141...2.17.142) Updates `http-client-spi` from 2.17.141 to 2.17.142 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.141...2.17.142) Updates `url-connection-client` from 2.17.141 to 2.17.142 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4f9a24ca6..42563d3aa 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.141 + 2.17.142 2.11.0 1.1.1 UTF-8 From 482f86c6b9f0b780809aa18cec4f3fbecdb0d4e5 Mon Sep 17 00:00:00 2001 From: William McCarthy <64298338+cjb574@users.noreply.github.com> Date: Sat, 5 Mar 2022 07:38:13 -0500 Subject: [PATCH 0065/1008] Documentation: Changed project name for consistency (#781) --- README.md | 2 +- docs/index.md | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a8a03350e..9c5aa9ae3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# AWS Lambda Powertools (Java) +# AWS Lambda Powertools for Java ![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) diff --git a/docs/index.md b/docs/index.md index e25844f52..ef66f4758 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- title: Homepage -description: AWS Lambda Powertools Java +description: AWS Lambda Powertools for Java --- ![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) diff --git a/mkdocs.yml b/mkdocs.yml index 151d1f779..e16d01450 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: Lambda Powertools Java +site_name: AWS Lambda Powertools for Java site_description: AWS Lambda Powertools for Java site_author: Amazon Web Services nav: diff --git a/pom.xml b/pom.xml index 42563d3aa..5d239ef34 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 1.12.0 pom - AWS Lambda Powertools Java library Parent + AWS Lambda Powertools for Java library Parent A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index e619cd7fc..cbf2f0225 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library Cloudformation + AWS Lambda Powertools for Java library Cloudformation A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index a65c52311..70ef22588 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library Core + AWS Lambda Powertools for Java library Core A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 9918753da..1f8feddcd 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -13,7 +13,7 @@ powertools-idempotency jar - AWS Lambda Powertools Java library Idempotency + AWS Lambda Powertools for Java library Idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index caf4e7073..2fca90dbd 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library Logging + AWS Lambda Powertools for Java library Logging A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 4c6c10d23..085e83889 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library Metrics + AWS Lambda Powertools for Java library Metrics A suite of utilities for AWS Lambda Functions that make creating custom metrics via AWS Embedded Metric Format asynchronously easier. diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index e2b7245ae..b255d8b13 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -12,7 +12,7 @@ powertools-parameters - AWS Lambda Powertools Java library Parameters + AWS Lambda Powertools for Java library Parameters Set of utilities to retrieve parameters from Secrets Manager or SSM Parameter Store diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 214f4575e..20b8dc756 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -13,7 +13,7 @@ powertools-serialization jar - AWS Lambda Powertools Java library Serialization Utilities + AWS Lambda Powertools for Java library Serialization Utilities diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index b701959ff..15a59bc4d 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library SQS + AWS Lambda Powertools for Java library SQS A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 518b7e995..c1ee0f33d 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library Test Suite + AWS Lambda Powertools for Java library Test Suite A suite of tests for interactions between the various Powertools modules. diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 13d23b2fd..2247f7e8f 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java library Tracing + AWS Lambda Powertools for Java library Tracing A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 8c548cef8..fc5735fa7 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -13,7 +13,7 @@ 1.12.0 - AWS Lambda Powertools Java validation library + AWS Lambda Powertools for Java validation library Json schema validation for Lambda events and responses From 9b63ad209473d35ed4f65ed98a0d18f8e173f170 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Mar 2022 12:10:13 +0100 Subject: [PATCH 0066/1008] build(deps): bump aws.sdk.version from 2.17.142 to 2.17.143 (#782) Bumps `aws.sdk.version` from 2.17.142 to 2.17.143. Updates `software.amazon.awssdk:bom` from 2.17.142 to 2.17.143 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.142...2.17.143) Updates `http-client-spi` from 2.17.142 to 2.17.143 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.142...2.17.143) Updates `url-connection-client` from 2.17.142 to 2.17.143 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d239ef34..ef36622bb 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.142 + 2.17.143 2.11.0 1.1.1 UTF-8 From 32dba3881778309e752c9809ee020c3ed13fe5e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 12:10:37 +0100 Subject: [PATCH 0067/1008] build(deps): bump aws.sdk.version from 2.17.143 to 2.17.144 (#785) Bumps `aws.sdk.version` from 2.17.143 to 2.17.144. Updates `software.amazon.awssdk:bom` from 2.17.143 to 2.17.144 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.143...2.17.144) Updates `http-client-spi` from 2.17.143 to 2.17.144 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.143...2.17.144) Updates `url-connection-client` from 2.17.143 to 2.17.144 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ef36622bb..7b3a9f573 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.143 + 2.17.144 2.11.0 1.1.1 UTF-8 From f5e8c132de836a7afea25c5ca32384fff85a179f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Mar 2022 12:11:07 +0100 Subject: [PATCH 0068/1008] build(deps): bump mockito-core from 4.3.1 to 4.4.0 (#787) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.3.1...v4.4.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7b3a9f573..6d132ebb7 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.3.1 + 4.4.0 test From 4386bd69afebeb7522f055b1de1dae09a1d106a2 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Wed, 9 Mar 2022 14:12:41 +0100 Subject: [PATCH 0069/1008] chore(docs): additional rename of project name (#781) (#789) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index ef66f4758..ae1899ef6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,7 +30,7 @@ Powertools dependencies are available in Maven Central. You can use your favouri **Quick hello world examples using SAM CLI** -You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including AWS Lambda Powertools Java. +You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including AWS Lambda Powertools for Java. ```bash sam init --location gh:aws-samples/cookiecutter-aws-sam-powertools-java From 11bc1486d288c3b22e4666dd616e09a22ae34fde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Mar 2022 12:10:54 +0100 Subject: [PATCH 0070/1008] build(deps): bump aws.sdk.version from 2.17.144 to 2.17.146 (#790) Bumps `aws.sdk.version` from 2.17.144 to 2.17.146. Updates `software.amazon.awssdk:bom` from 2.17.144 to 2.17.146 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.144...2.17.146) Updates `http-client-spi` from 2.17.144 to 2.17.146 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.144...2.17.146) Updates `url-connection-client` from 2.17.144 to 2.17.146 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d132ebb7..8261f8525 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.144 + 2.17.146 2.11.0 1.1.1 UTF-8 From f8f3893912817b07685a9d61c0f68c6cc79a08db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Mar 2022 12:10:30 +0100 Subject: [PATCH 0071/1008] build(deps): bump aws.sdk.version from 2.17.146 to 2.17.147 (#792) Bumps `aws.sdk.version` from 2.17.146 to 2.17.147. Updates `software.amazon.awssdk:bom` from 2.17.146 to 2.17.147 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.146...2.17.147) Updates `http-client-spi` from 2.17.146 to 2.17.147 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.146...2.17.147) Updates `url-connection-client` from 2.17.146 to 2.17.147 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8261f8525..d8cfb3bdd 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.146 + 2.17.147 2.11.0 1.1.1 UTF-8 From e1f1f082b0b10eb63dc7e3c964915360d2b2a0e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Mar 2022 12:18:12 +0100 Subject: [PATCH 0072/1008] build(deps-dev): bump junit-pioneer from 1.6.1 to 1.6.2 (#793) Bumps [junit-pioneer](https://github.com/junit-pioneer/junit-pioneer) from 1.6.1 to 1.6.2. - [Release notes](https://github.com/junit-pioneer/junit-pioneer/releases) - [Commits](https://github.com/junit-pioneer/junit-pioneer/compare/v1.6.1...v1.6.2) --- updated-dependencies: - dependency-name: org.junit-pioneer:junit-pioneer dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 1f8feddcd..0332bf8e4 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -91,7 +91,7 @@ org.junit-pioneer junit-pioneer - 1.6.1 + 1.6.2 test From 812ecebfde6733941ad1a3f81c62316015a92c7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:16:18 +0100 Subject: [PATCH 0073/1008] build(deps): bump aws.sdk.version from 2.17.147 to 2.17.148 (#794) Bumps `aws.sdk.version` from 2.17.147 to 2.17.148. Updates `software.amazon.awssdk:bom` from 2.17.147 to 2.17.148 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.147...2.17.148) Updates `http-client-spi` from 2.17.147 to 2.17.148 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.147...2.17.148) Updates `url-connection-client` from 2.17.147 to 2.17.148 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d8cfb3bdd..69192b160 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.147 + 2.17.148 2.11.0 1.1.1 UTF-8 From b568111773ab59c550a3a3840899d884960c6207 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 12:09:13 +0100 Subject: [PATCH 0074/1008] build(deps): bump aws.sdk.version from 2.17.148 to 2.17.149 (#795) Bumps `aws.sdk.version` from 2.17.148 to 2.17.149. Updates `software.amazon.awssdk:bom` from 2.17.148 to 2.17.149 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.148...2.17.149) Updates `http-client-spi` from 2.17.148 to 2.17.149 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.148...2.17.149) Updates `url-connection-client` from 2.17.148 to 2.17.149 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69192b160..beb403827 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.1 1.9.7 - 2.17.148 + 2.17.149 2.11.0 1.1.1 UTF-8 From ab46aab464afd6be186771beaebd73984bf623e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:07:59 +0100 Subject: [PATCH 0075/1008] build(deps): bump maven-compiler-plugin from 3.10.0 to 3.10.1 (#791) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.0 to 3.10.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.0...maven-compiler-plugin-3.10.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index beb403827..d069f05e0 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 1.2.1 3.11.0 1.0.0 - 3.10.0 + 3.10.1 1.14.0 2.22.2 0.8.7 From 6e09da2c6bc36ad41f3c9f049992eef634781787 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:08:20 +0100 Subject: [PATCH 0076/1008] build(deps): bump json-schema-validator from 1.0.66 to 1.0.67 (#783) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.66 to 1.0.67. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.66...1.0.67) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index fc5735fa7..dca525ed0 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.66 + 1.0.67 From a9aaa8f763f70fbfa379e2e4920af844fb4da19d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:53:39 +0100 Subject: [PATCH 0077/1008] build(deps): bump jackson-databind from 2.13.1 to 2.13.2 (#784) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.1 to 2.13.2. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d069f05e0..47eecb93a 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.17.2 - 2.13.1 + 2.13.2 1.9.7 2.17.149 2.11.0 From 6181567c7a5a5a4b2c588fb5e332ac448a407e3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:53:49 +0100 Subject: [PATCH 0078/1008] build(deps): bump mockito-inline from 4.3.1 to 4.4.0 (#788) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.3.1...v4.4.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47eecb93a..88d213d15 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.3.1 + 4.4.0 test From 67605975a2952c38ec21e314486cae495a01732f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Mar 2022 12:10:38 +0100 Subject: [PATCH 0079/1008] build(deps): bump aws.sdk.version from 2.17.149 to 2.17.150 (#798) Bumps `aws.sdk.version` from 2.17.149 to 2.17.150. Updates `software.amazon.awssdk:bom` from 2.17.149 to 2.17.150 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.149...2.17.150) Updates `http-client-spi` from 2.17.149 to 2.17.150 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.149...2.17.150) Updates `url-connection-client` from 2.17.149 to 2.17.150 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 88d213d15..513d2803b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2 1.9.7 - 2.17.149 + 2.17.150 2.11.0 1.1.1 UTF-8 From eae5fff7e4ef0662f4519343b51065c65d4dc51a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Mar 2022 12:11:41 +0100 Subject: [PATCH 0080/1008] build(deps): bump aws.sdk.version from 2.17.150 to 2.17.151 (#799) Bumps `aws.sdk.version` from 2.17.150 to 2.17.151. Updates `software.amazon.awssdk:bom` from 2.17.150 to 2.17.151 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.150...2.17.151) Updates `http-client-spi` from 2.17.150 to 2.17.151 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.150...2.17.151) Updates `url-connection-client` from 2.17.150 to 2.17.151 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 513d2803b..2f417d8ff 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2 1.9.7 - 2.17.150 + 2.17.151 2.11.0 1.1.1 UTF-8 From ee767eefb18f77f7191c62cc5cd01e027b6733a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Fri, 18 Mar 2022 13:42:28 +0100 Subject: [PATCH 0081/1008] fix: disable idempotency doesn't disable dynamodb client creation in persistent store (#796) * do not create DynamoDbClient when idempotency is disabled * ignoring case for idempotency disable env --- .../idempotency/internal/IdempotentAspect.java | 2 +- .../persistence/DynamoDBPersistenceStore.java | 17 ++++++++++++----- .../DynamoDBPersistenceStoreTest.java | 9 +++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index b372a34a4..088e81249 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -47,7 +47,7 @@ public Object around(ProceedingJoinPoint pjp, Idempotent idempotent) throws Throwable { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); - if (idempotencyDisabledEnv != null && !idempotencyDisabledEnv.equals("false")) { + if (idempotencyDisabledEnv != null && !idempotencyDisabledEnv.equalsIgnoreCase("false")) { return pjp.proceed(pjp.getArgs()); } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 6e36c6dc6..4985f845d 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -78,11 +78,18 @@ private DynamoDBPersistenceStore(String tableName, if (client != null) { this.dynamoDbClient = client; } else { - DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder() - .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) - .httpClient(UrlConnectionHttpClient.builder().build()) - .region(Region.of(System.getenv(AWS_REGION_ENV))); - this.dynamoDbClient = ddbBuilder.build(); + String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); + if (idempotencyDisabledEnv == null || idempotencyDisabledEnv.equalsIgnoreCase("false")) { + DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv(AWS_REGION_ENV))); + this.dynamoDbClient = ddbBuilder.build(); + } else { + // we do not want to create a DynamoDbClient if idempotency is disabled + // null is ok as idempotency won't be called + this.dynamoDbClient = null; + } } } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java index ecf8ad3e0..0da516324 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java @@ -16,7 +16,9 @@ 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.*; +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; @@ -260,6 +262,13 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou } } + @Test + @SetEnvironmentVariable(key = Constants.IDEMPOTENCY_DISABLED_ENV, value = "true") + public void idempotencyDisabled_noClientShouldBeCreated() { + DynamoDBPersistenceStore store = DynamoDBPersistenceStore.builder().withTableName(TABLE_NAME).build(); + assertThatThrownBy(() -> store.getRecord("fake")).isInstanceOf(NullPointerException.class); + } + @BeforeEach public void setup() { dynamoDBPersistenceStore = DynamoDBPersistenceStore.builder() From 9a11e331499a68add4ef76ff80b482be5ceec4f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Mar 2022 12:23:55 +0100 Subject: [PATCH 0082/1008] build(deps): bump aws.sdk.version from 2.17.151 to 2.17.152 (#800) Bumps `aws.sdk.version` from 2.17.151 to 2.17.152. Updates `software.amazon.awssdk:bom` from 2.17.151 to 2.17.152 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.151...2.17.152) Updates `http-client-spi` from 2.17.151 to 2.17.152 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.151...2.17.152) Updates `url-connection-client` from 2.17.151 to 2.17.152 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f417d8ff..977378bb4 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2 1.9.7 - 2.17.151 + 2.17.152 2.11.0 1.1.1 UTF-8 From 54fe7a01c52b23179be8b0217e02974fa0cc4ed5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Mar 2022 12:10:22 +0100 Subject: [PATCH 0083/1008] build(deps): bump aws.sdk.version from 2.17.152 to 2.17.153 (#801) Bumps `aws.sdk.version` from 2.17.152 to 2.17.153. Updates `software.amazon.awssdk:bom` from 2.17.152 to 2.17.153 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.152...2.17.153) Updates `http-client-spi` from 2.17.152 to 2.17.153 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.152...2.17.153) Updates `url-connection-client` from 2.17.152 to 2.17.153 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 977378bb4..8705b915d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2 1.9.7 - 2.17.152 + 2.17.153 2.11.0 1.1.1 UTF-8 From eb2bb39954c0c2c52b90954ba627747264f4fd8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Mar 2022 12:41:06 +0100 Subject: [PATCH 0084/1008] build(deps): bump aws.sdk.version from 2.17.153 to 2.17.154 (#802) Bumps `aws.sdk.version` from 2.17.153 to 2.17.154. Updates `software.amazon.awssdk:bom` from 2.17.153 to 2.17.154 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.153...2.17.154) Updates `http-client-spi` from 2.17.153 to 2.17.154 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.153...2.17.154) Updates `url-connection-client` from 2.17.153 to 2.17.154 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8705b915d..791718227 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2 1.9.7 - 2.17.153 + 2.17.154 2.11.0 1.1.1 UTF-8 From 7217317cd24a329d8dbd0c5cb7e410c5e2bc60c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 12:11:11 +0100 Subject: [PATCH 0085/1008] build(deps): bump aws.sdk.version from 2.17.154 to 2.17.155 (#804) Bumps `aws.sdk.version` from 2.17.154 to 2.17.155. Updates `software.amazon.awssdk:bom` from 2.17.154 to 2.17.155 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.154...2.17.155) Updates `http-client-spi` from 2.17.154 to 2.17.155 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.154...2.17.155) Updates `url-connection-client` from 2.17.154 to 2.17.155 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 791718227..f4fab7c6f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2 1.9.7 - 2.17.154 + 2.17.155 2.11.0 1.1.1 UTF-8 From a591c1eebdf02482041c05942557273f8380e91a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:10:37 +0100 Subject: [PATCH 0086/1008] build(deps): bump jackson-databind from 2.13.2 to 2.13.2.1 (#807) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.2 to 2.13.2.1. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f4fab7c6f..f850cce23 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.17.2 - 2.13.2 + 2.13.2.1 1.9.7 2.17.155 2.11.0 From d66038661e979362ef3a88d4c4e9626d821e2f17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Mar 2022 13:11:07 +0200 Subject: [PATCH 0087/1008] build(deps): bump json-schema-validator from 1.0.67 to 1.0.68 (#809) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.67 to 1.0.68. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.67...1.0.68) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index dca525ed0..2f95c6626 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.67 + 1.0.68 From 3614f09d948a59e4339ca0051ebcd31668ecdc74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 13:15:42 +0200 Subject: [PATCH 0088/1008] build(deps): bump jackson-databind from 2.13.2.1 to 2.13.2.2 (#811) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.2.1 to 2.13.2.2. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f850cce23..bde1fa370 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.17.2 - 2.13.2.1 + 2.13.2.2 1.9.7 2.17.155 2.11.0 From d705d279edd090fdbba53d73fe9f9e654582873d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 13:11:42 +0200 Subject: [PATCH 0089/1008] build(deps): bump aws.sdk.version from 2.17.155 to 2.17.159 (#812) Bumps `aws.sdk.version` from 2.17.155 to 2.17.159. Updates `software.amazon.awssdk:bom` from 2.17.155 to 2.17.159 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.155...2.17.159) Updates `http-client-spi` from 2.17.155 to 2.17.159 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.155...2.17.159) Updates `url-connection-client` from 2.17.155 to 2.17.159 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bde1fa370..d33b4ba93 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.155 + 2.17.159 2.11.0 1.1.1 UTF-8 From 9091f45aa8ee0bb9547e8a9a362efdf426ab9c17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 13:10:30 +0200 Subject: [PATCH 0090/1008] build(deps): bump aws.sdk.version from 2.17.159 to 2.17.160 (#813) Bumps `aws.sdk.version` from 2.17.159 to 2.17.160. Updates `software.amazon.awssdk:bom` from 2.17.159 to 2.17.160 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.159...2.17.160) Updates `http-client-spi` from 2.17.159 to 2.17.160 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.159...2.17.160) Updates `url-connection-client` from 2.17.159 to 2.17.160 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d33b4ba93..30de643ad 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.159 + 2.17.160 2.11.0 1.1.1 UTF-8 From 52a1d03c79dbf312d77af49ae41f25d88fed0455 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:10:43 +0200 Subject: [PATCH 0091/1008] build(deps): bump aws.sdk.version from 2.17.160 to 2.17.161 (#814) Bumps `aws.sdk.version` from 2.17.160 to 2.17.161. Updates `software.amazon.awssdk:bom` from 2.17.160 to 2.17.161 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.160...2.17.161) Updates `http-client-spi` from 2.17.160 to 2.17.161 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.160...2.17.161) Updates `url-connection-client` from 2.17.160 to 2.17.161 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 30de643ad..531f202a7 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.160 + 2.17.161 2.11.0 1.1.1 UTF-8 From e3a36c9409263a6b36e62097321f4cc72ec17d67 Mon Sep 17 00:00:00 2001 From: Hamid Mortazavi Date: Mon, 4 Apr 2022 12:08:10 +0100 Subject: [PATCH 0092/1008] Fixed thread-safety issue of MessageDigest (#817) --- .../persistence/BasePersistenceStore.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index a65b4c193..db0aaa688 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -60,7 +60,7 @@ public abstract class BasePersistenceStore implements PersistenceStore { protected boolean payloadValidationEnabled = false; private Expression validationKeyJMESPath; private boolean throwOnNoIdempotencyKey = false; - private MessageDigest hashAlgorithm; + private String hashFunctionName; /** * Initialize the base persistence layer from the configuration settings @@ -95,17 +95,7 @@ public void configure(IdempotencyConfig config, String functionName) { cache = new LRUCache<>(config.getLocalCacheMaxItems()); } expirationInSeconds = config.getExpirationInSeconds(); - - try { - hashAlgorithm = MessageDigest.getInstance(config.getHashFunction()); - } catch (NoSuchAlgorithmException e) { - LOG.warn("Error instantiating {} hash function, trying with MD5", config.getHashFunction()); - try { - hashAlgorithm = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException("Unable to instantiate MD5 digest", ex); - } - } + hashFunctionName = config.getHashFunction(); configured = true; } @@ -275,10 +265,27 @@ String generateHash(JsonNode data) { } else if (data.isBoolean()) { node = data.asBoolean(); } else node = data; // anything else + + MessageDigest hashAlgorithm = getHashAlgorithm(); byte[] digest = hashAlgorithm.digest(node.toString().getBytes(StandardCharsets.UTF_8)); return String.format("%032x", new BigInteger(1, digest)); } + private MessageDigest getHashAlgorithm() { + MessageDigest hashAlgorithm; + try { + hashAlgorithm = MessageDigest.getInstance(hashFunctionName); + } catch (NoSuchAlgorithmException e) { + LOG.warn("Error instantiating {} hash function, trying with MD5", hashFunctionName); + try { + hashAlgorithm = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException("Unable to instantiate MD5 digest", ex); + } + } + return hashAlgorithm; + } + /** * Validate that the hashed payload matches data provided and stored data record * From 6bc47b99f2a565f1d8e28098fcf4ee026ba69c88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 13:14:59 +0200 Subject: [PATCH 0093/1008] build(deps): bump aws.sdk.version from 2.17.161 to 2.17.162 (#818) Bumps `aws.sdk.version` from 2.17.161 to 2.17.162. Updates `software.amazon.awssdk:bom` from 2.17.161 to 2.17.162 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.161...2.17.162) Updates `http-client-spi` from 2.17.161 to 2.17.162 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.161...2.17.162) Updates `url-connection-client` from 2.17.161 to 2.17.162 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 531f202a7..bcf772cc8 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.161 + 2.17.162 2.11.0 1.1.1 UTF-8 From 6c3ae5f9114ca7410191125df4a5fdcf651ddbe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 13:17:10 +0200 Subject: [PATCH 0094/1008] build(deps): bump spotbugs-maven-plugin from 4.5.3.0 to 4.6.0.0 (#806) Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.5.3.0 to 4.6.0.0. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.5.3.0...spotbugs-maven-plugin-4.6.0.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bcf772cc8..07e0f3d1e 100644 --- a/pom.xml +++ b/pom.xml @@ -457,7 +457,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.5.3.0 + 4.6.0.0 test From 426ddae17695337a8fddce05060a7aa889b2eee5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 13:10:37 +0200 Subject: [PATCH 0095/1008] build(deps): bump jacoco-maven-plugin from 0.8.7 to 0.8.8 (#820) Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.7 to 0.8.8. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.7...v0.8.8) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 07e0f3d1e..dab0195c9 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 3.10.1 1.14.0 2.22.2 - 0.8.7 + 0.8.8 2.7 1.6.8 3.3.2 From b7e96f6ffe1d6dde514e264925fbc905b1b3cbbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Apr 2022 13:10:42 +0200 Subject: [PATCH 0096/1008] build(deps): bump aws.sdk.version from 2.17.162 to 2.17.164 (#821) Bumps `aws.sdk.version` from 2.17.162 to 2.17.164. Updates `software.amazon.awssdk:bom` from 2.17.162 to 2.17.164 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.162...2.17.164) Updates `http-client-spi` from 2.17.162 to 2.17.164 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.162...2.17.164) Updates `url-connection-client` from 2.17.162 to 2.17.164 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dab0195c9..258639f36 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.162 + 2.17.164 2.11.0 1.1.1 UTF-8 From dbeabe9d77a1636cad5c1019c7c2f4e0c2ab10e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Apr 2022 13:09:50 +0200 Subject: [PATCH 0097/1008] build(deps): bump aws.sdk.version from 2.17.164 to 2.17.165 (#822) Bumps `aws.sdk.version` from 2.17.164 to 2.17.165. Updates `software.amazon.awssdk:bom` from 2.17.164 to 2.17.165 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.164...2.17.165) Updates `http-client-spi` from 2.17.164 to 2.17.165 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.164...2.17.165) Updates `url-connection-client` from 2.17.164 to 2.17.165 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 258639f36..5575439dd 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.164 + 2.17.165 2.11.0 1.1.1 UTF-8 From a207202214cffcc93f9c7ff270d11e05a48527d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 13:10:56 +0200 Subject: [PATCH 0098/1008] build(deps): bump aws.xray.recorder.version from 2.11.0 to 2.11.1 (#823) Bumps `aws.xray.recorder.version` from 2.11.0 to 2.11.1. Updates `aws-xray-recorder-sdk-core` from 2.11.0 to 2.11.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.0...v2.11.1) Updates `aws-xray-recorder-sdk-aws-sdk-core` from 2.11.0 to 2.11.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.0...v2.11.1) Updates `aws-xray-recorder-sdk-aws-sdk-v2` from 2.11.0 to 2.11.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.0...v2.11.1) Updates `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.11.0 to 2.11.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.0...v2.11.1) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5575439dd..55de7f0d2 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 2.13.2.2 1.9.7 2.17.165 - 2.11.0 + 2.11.1 1.1.1 UTF-8 1.2.1 From 51f2a4ac218ab948477ff829ef7630baec3ee360 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 13:19:13 +0200 Subject: [PATCH 0099/1008] build(deps): bump aws.sdk.version from 2.17.165 to 2.17.166 (#824) Bumps `aws.sdk.version` from 2.17.165 to 2.17.166. Updates `software.amazon.awssdk:bom` from 2.17.165 to 2.17.166 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.165...2.17.166) Updates `http-client-spi` from 2.17.165 to 2.17.166 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.165...2.17.166) Updates `url-connection-client` from 2.17.165 to 2.17.166 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55de7f0d2..c38099545 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.165 + 2.17.166 2.11.1 1.1.1 UTF-8 From f53c42fb82161cb4c91655b191492c3fb80d21f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 13:11:27 +0200 Subject: [PATCH 0100/1008] build(deps-dev): bump junit-pioneer from 1.6.2 to 1.7.0 (#828) Bumps [junit-pioneer](https://github.com/junit-pioneer/junit-pioneer) from 1.6.2 to 1.7.0. - [Release notes](https://github.com/junit-pioneer/junit-pioneer/releases) - [Commits](https://github.com/junit-pioneer/junit-pioneer/compare/v1.6.2...v1.7.0) --- updated-dependencies: - dependency-name: org.junit-pioneer:junit-pioneer dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 0332bf8e4..e588b5302 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -91,7 +91,7 @@ org.junit-pioneer junit-pioneer - 1.6.2 + 1.7.0 test From 7b67f5c7cab1aac8596439a9927804ea7d678a1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Apr 2022 15:39:23 +0200 Subject: [PATCH 0101/1008] build(deps): bump aws.sdk.version from 2.17.166 to 2.17.168 (#830) Bumps `aws.sdk.version` from 2.17.166 to 2.17.168. Updates `software.amazon.awssdk:bom` from 2.17.166 to 2.17.168 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.166...2.17.168) Updates `http-client-spi` from 2.17.166 to 2.17.168 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.166...2.17.168) Updates `url-connection-client` from 2.17.166 to 2.17.168 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c38099545..ea370b26f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.166 + 2.17.168 2.11.1 1.1.1 UTF-8 From 9e1f6de5498a4866a075ad44749ee6c181e0a44a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Apr 2022 13:11:21 +0200 Subject: [PATCH 0102/1008] build(deps): bump aws.sdk.version from 2.17.168 to 2.17.169 (#832) Bumps `aws.sdk.version` from 2.17.168 to 2.17.169. Updates `software.amazon.awssdk:bom` from 2.17.168 to 2.17.169 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.168...2.17.169) Updates `http-client-spi` from 2.17.168 to 2.17.169 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.168...2.17.169) Updates `url-connection-client` from 2.17.168 to 2.17.169 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea370b26f..73d5e7d62 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.168 + 2.17.169 2.11.1 1.1.1 UTF-8 From 96cb9034a3a71918233feaac8a2c252e6531b077 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 13:09:48 +0200 Subject: [PATCH 0103/1008] build(deps): bump aws.sdk.version from 2.17.169 to 2.17.170 (#833) Bumps `aws.sdk.version` from 2.17.169 to 2.17.170. Updates `software.amazon.awssdk:bom` from 2.17.169 to 2.17.170 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.169...2.17.170) Updates `http-client-spi` from 2.17.169 to 2.17.170 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.169...2.17.170) Updates `url-connection-client` from 2.17.169 to 2.17.170 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73d5e7d62..0e2d59110 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.169 + 2.17.170 2.11.1 1.1.1 UTF-8 From 7e583c891ca9e059dd7ba968bf7404d6d5f1532e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Apr 2022 13:09:56 +0200 Subject: [PATCH 0104/1008] build(deps): bump aws.sdk.version from 2.17.170 to 2.17.171 (#834) Bumps `aws.sdk.version` from 2.17.170 to 2.17.171. Updates `software.amazon.awssdk:bom` from 2.17.170 to 2.17.171 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.170...2.17.171) Updates `http-client-spi` from 2.17.170 to 2.17.171 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.170...2.17.171) Updates `url-connection-client` from 2.17.170 to 2.17.171 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0e2d59110..ed7bcf384 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.170 + 2.17.171 2.11.1 1.1.1 UTF-8 From b793dd31ee717c1144b1fbe0b346874fdd8e4958 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Apr 2022 13:11:11 +0200 Subject: [PATCH 0105/1008] build(deps): bump aws.sdk.version from 2.17.171 to 2.17.172 (#836) Bumps `aws.sdk.version` from 2.17.171 to 2.17.172. Updates `software.amazon.awssdk:bom` from 2.17.171 to 2.17.172 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.171...2.17.172) Updates `http-client-spi` from 2.17.171 to 2.17.172 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.171...2.17.172) Updates `url-connection-client` from 2.17.171 to 2.17.172 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed7bcf384..8c192e596 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.171 + 2.17.172 2.11.1 1.1.1 UTF-8 From 62e315bc92f4be097a51cfbdfdcc9c9323497063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 13:10:24 +0200 Subject: [PATCH 0106/1008] build(deps): bump json-schema-validator from 1.0.68 to 1.0.69 (#837) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.68 to 1.0.69. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.68...1.0.69) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 2f95c6626..f14977e38 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.68 + 1.0.69 From 719079a1bba6cb99304a140274ecc9e7772318a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:13:38 +0200 Subject: [PATCH 0107/1008] build(deps): bump aws.sdk.version from 2.17.172 to 2.17.173 (#838) Bumps `aws.sdk.version` from 2.17.172 to 2.17.173. Updates `software.amazon.awssdk:bom` from 2.17.172 to 2.17.173 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.172...2.17.173) Updates `http-client-spi` from 2.17.172 to 2.17.173 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.172...2.17.173) Updates `url-connection-client` from 2.17.172 to 2.17.173 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8c192e596..fe5348e96 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.172 + 2.17.173 2.11.1 1.1.1 UTF-8 From 820c0abc2b36e437e97dff6d9a0457c2ac0a2105 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 09:03:04 +0200 Subject: [PATCH 0108/1008] build(deps): bump mockito-core from 4.4.0 to 4.5.0 (#839) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.4.0...v4.5.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fe5348e96..d0df95b1a 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.0 test From 41c46d4b6c6faa739f5a2e7e343c99ca35eb4443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 09:03:13 +0200 Subject: [PATCH 0109/1008] build(deps): bump mockito-inline from 4.4.0 to 4.5.0 (#840) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.4.0...v4.5.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0df95b1a..850f63995 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.4.0 + 4.5.0 test From cba1b1ec86e05250d24c018e7208bc56718e190e Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 21 Apr 2022 09:22:08 +0200 Subject: [PATCH 0110/1008] chore:Prep release 1.12.1 (#841) * chore:prep release 1.12.1 * Update CHANGELOG.md Co-authored-by: pankajagrawal16 --- CHANGELOG.md | 13 +++++++++++++ README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 29 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 887cd6287..5662a4538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,19 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.12.1] - 2022-04-21 + +### Bug Fixes + +* **Idempotency**: thread-safety issue of MessageDigest ([#817](https://github.com/awslabs/aws-lambda-powertools-java/pull/817)) +* **Idempotency**: disable dynamodb client creation in persistent store when disabling idempotency ([#796](https://github.com/awslabs/aws-lambda-powertools-java/pull/796)) + + +## Maintenance + +* **deps**: Bump third party dependencies to the latest versions. + + ## [1.12.0] - 2022-03-01 ### Added diff --git a/README.md b/README.md index 9c5aa9ae3..fd750a5b9 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.12.0 + 1.12.1 software.amazon.lambda powertools-logging - 1.12.0 + 1.12.1 software.amazon.lambda powertools-metrics - 1.12.0 + 1.12.1 ... diff --git a/mkdocs.yml b/mkdocs.yml index e16d01450..ba7925c2d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,7 +77,7 @@ extra_javascript: extra: powertools: - version: 1.12.0 + version: 1.12.1 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index 850f63995..4ebbb3aef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.12.0 + 1.12.1 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index cbf2f0225..c68e02cb7 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 70ef22588..94fb75be5 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index e588b5302..d14226cb0 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.12.0 + 1.12.1 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 2fca90dbd..5d1fce836 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 085e83889..226f646cb 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index b255d8b13..196aa17e2 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 20b8dc756..1748838ca 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 15a59bc4d..bdeafe273 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index c1ee0f33d..f61241022 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 2247f7e8f..e39780ce4 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index f14977e38..924c61cfe 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.0 + 1.12.1 AWS Lambda Powertools for Java validation library From 6a86e0fe22db9e8e971363e005e4b08f59528412 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 21 Apr 2022 09:26:45 +0200 Subject: [PATCH 0111/1008] chore: correct bug fix release number --- CHANGELOG.md | 2 +- README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5662a4538..d877f08f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] -## [1.12.1] - 2022-04-21 +## [1.20.1] - 2022-04-21 ### Bug Fixes diff --git a/README.md b/README.md index fd750a5b9..49332da45 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.12.1 + 1.20.1 software.amazon.lambda powertools-logging - 1.12.1 + 1.20.1 software.amazon.lambda powertools-metrics - 1.12.1 + 1.20.1 ... diff --git a/mkdocs.yml b/mkdocs.yml index ba7925c2d..fc1678d13 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,7 +77,7 @@ extra_javascript: extra: powertools: - version: 1.12.1 + version: 1.20.1 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index 4ebbb3aef..c7086c521 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.12.1 + 1.20.1 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index c68e02cb7..38af3a5a0 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 94fb75be5..1c7f36f16 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index d14226cb0..8d6a99343 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.12.1 + 1.20.1 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 5d1fce836..d526af797 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 226f646cb..4b94a19c1 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 196aa17e2..6e231706f 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 1748838ca..73fce7e80 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index bdeafe273..6bd3f9378 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index f61241022..a0dbe5004 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index e39780ce4..cf5c8a7a4 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 924c61cfe..626857289 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.20.1 AWS Lambda Powertools for Java validation library From 162b7f9fb99acb1838b5bbde069ee67fa27851be Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 21 Apr 2022 09:34:34 +0200 Subject: [PATCH 0112/1008] Revert "chore: correct bug fix release number" This reverts commit 6a86e0fe22db9e8e971363e005e4b08f59528412. --- CHANGELOG.md | 2 +- README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d877f08f4..5662a4538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] -## [1.20.1] - 2022-04-21 +## [1.12.1] - 2022-04-21 ### Bug Fixes diff --git a/README.md b/README.md index 49332da45..fd750a5b9 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.20.1 + 1.12.1 software.amazon.lambda powertools-logging - 1.20.1 + 1.12.1 software.amazon.lambda powertools-metrics - 1.20.1 + 1.12.1 ... diff --git a/mkdocs.yml b/mkdocs.yml index fc1678d13..ba7925c2d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,7 +77,7 @@ extra_javascript: extra: powertools: - version: 1.20.1 + version: 1.12.1 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index c7086c521..4ebbb3aef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.20.1 + 1.12.1 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 38af3a5a0..c68e02cb7 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 1c7f36f16..94fb75be5 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 8d6a99343..d14226cb0 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.20.1 + 1.12.1 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index d526af797..5d1fce836 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 4b94a19c1..226f646cb 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 6e231706f..196aa17e2 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 73fce7e80..1748838ca 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 6bd3f9378..bdeafe273 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index a0dbe5004..f61241022 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index cf5c8a7a4..e39780ce4 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 626857289..924c61cfe 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.20.1 + 1.12.1 AWS Lambda Powertools for Java validation library From 5a89c37189114d1b733d269ac53dffe394eae37f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 13:10:04 +0200 Subject: [PATCH 0113/1008] build(deps): bump maven-javadoc-plugin from 3.3.2 to 3.4.0 (#843) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.2 to 3.4.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.2...maven-javadoc-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ebbb3aef..a7056eb8c 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 0.8.8 2.7 1.6.8 - 3.3.2 + 3.4.0 3.2.1 3.0.1 5.8.2 From f423d94c965b2df0de9ad990ac60746dc42f9f6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Apr 2022 13:11:24 +0200 Subject: [PATCH 0114/1008] build(deps): bump mockito-inline from 4.5.0 to 4.5.1 (#844) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.5.0...v4.5.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7056eb8c..d49cdfc38 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.5.0 + 4.5.1 test From dcc85dde6df4b598a7964eb5c5a82b7a6e9b640f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 13:10:11 +0200 Subject: [PATCH 0115/1008] build(deps): bump aws.sdk.version from 2.17.173 to 2.17.176 (#847) Bumps `aws.sdk.version` from 2.17.173 to 2.17.176. Updates `software.amazon.awssdk:bom` from 2.17.173 to 2.17.176 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.173...2.17.176) Updates `http-client-spi` from 2.17.173 to 2.17.176 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.173...2.17.176) Updates `url-connection-client` from 2.17.173 to 2.17.176 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d49cdfc38..40293f94b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.173 + 2.17.176 2.11.1 1.1.1 UTF-8 From 6f7e044dac5af7749e053cd9ded3228e1adb6bca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 13:11:10 +0200 Subject: [PATCH 0116/1008] build(deps): bump aws.sdk.version from 2.17.176 to 2.17.177 (#848) Bumps `aws.sdk.version` from 2.17.176 to 2.17.177. Updates `software.amazon.awssdk:bom` from 2.17.176 to 2.17.177 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.176...2.17.177) Updates `http-client-spi` from 2.17.176 to 2.17.177 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.176...2.17.177) Updates `url-connection-client` from 2.17.176 to 2.17.177 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40293f94b..793582fee 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.176 + 2.17.177 2.11.1 1.1.1 UTF-8 From ec9649c74b7e14285c640c1fae35d6b60fb30a73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Apr 2022 14:02:17 +0200 Subject: [PATCH 0117/1008] build(deps): bump aws.sdk.version from 2.17.177 to 2.17.178 (#849) Bumps `aws.sdk.version` from 2.17.177 to 2.17.178. Updates `software.amazon.awssdk:bom` from 2.17.177 to 2.17.178 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.177...2.17.178) Updates `http-client-spi` from 2.17.177 to 2.17.178 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.177...2.17.178) Updates `url-connection-client` from 2.17.177 to 2.17.178 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 793582fee..67b90cb3e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.177 + 2.17.178 2.11.1 1.1.1 UTF-8 From ac27cfcad01a947a9a216d20555a91f1a1bba7f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Apr 2022 13:10:53 +0200 Subject: [PATCH 0118/1008] build(deps): bump aws.sdk.version from 2.17.178 to 2.17.179 (#850) Bumps `aws.sdk.version` from 2.17.178 to 2.17.179. Updates `software.amazon.awssdk:bom` from 2.17.178 to 2.17.179 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.178...2.17.179) Updates `http-client-spi` from 2.17.178 to 2.17.179 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.178...2.17.179) Updates `url-connection-client` from 2.17.178 to 2.17.179 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 67b90cb3e..a351ebff5 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.178 + 2.17.179 2.11.1 1.1.1 UTF-8 From 896cacd050deca0bb3262581f76f59a484fbb920 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:01:11 +0200 Subject: [PATCH 0119/1008] build(deps): bump mockito-core from 4.5.0 to 4.5.1 (#846) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.5.0...v4.5.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a351ebff5..fac37db84 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.5.0 + 4.5.1 test From d829a14e59b263875bc0480a3782b490232cb002 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Thu, 28 Apr 2022 23:13:10 +0200 Subject: [PATCH 0120/1008] fix: remove local implementation of PayloadS3Pointer.java and use payloadoffloading-common (#851) --- pom.xml | 2 +- powertools-sqs/pom.xml | 4 ++ .../sqs/internal/SqsLargeMessageAspect.java | 3 +- .../payloadoffloading/PayloadS3Pointer.java | 59 ------------------- 4 files changed, 7 insertions(+), 61 deletions(-) delete mode 100644 powertools-sqs/src/main/java/software/amazon/payloadoffloading/PayloadS3Pointer.java diff --git a/pom.xml b/pom.xml index fac37db84..5e5bb3f9b 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 1.9.7 2.17.179 2.11.1 - 1.1.1 + 2.1.2 UTF-8 1.2.1 3.11.0 diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index bdeafe273..76abe5a93 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -49,6 +49,10 @@ com.amazonaws aws-lambda-java-core + + software.amazon.payloadoffloading + payloadoffloading-common + com.amazonaws aws-lambda-java-events 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 index 072d903d0..588d434d7 100644 --- 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 @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Function; import com.amazonaws.services.lambda.runtime.Context; @@ -68,7 +69,7 @@ public static List processMessages(final List reco for (SQSMessage sqsMessage : records) { if (isBodyLargeMessagePointer(sqsMessage.getBody())) { - PayloadS3Pointer s3Pointer = PayloadS3Pointer.fromJson(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 s3Object = callS3Gracefully(s3Pointer, pointer -> { diff --git a/powertools-sqs/src/main/java/software/amazon/payloadoffloading/PayloadS3Pointer.java b/powertools-sqs/src/main/java/software/amazon/payloadoffloading/PayloadS3Pointer.java deleted file mode 100644 index 078b9a773..000000000 --- a/powertools-sqs/src/main/java/software/amazon/payloadoffloading/PayloadS3Pointer.java +++ /dev/null @@ -1,59 +0,0 @@ -package software.amazon.payloadoffloading; - -import java.util.Optional; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.SerializationFeature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static java.util.Optional.empty; -import static java.util.Optional.ofNullable; - -public class PayloadS3Pointer { - private static final Logger LOG = LoggerFactory.getLogger(PayloadS3Pointer.class); - private static final ObjectMapper objectMapper = new ObjectMapper(); - - private String s3BucketName; - private String s3Key; - - static { - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); - } - - private PayloadS3Pointer() { - - } - - public String getS3BucketName() { - return this.s3BucketName; - } - - public String getS3Key() { - return this.s3Key; - } - - public static Optional fromJson(String s3PointerJson) { - try { - return ofNullable(objectMapper.readValue(s3PointerJson, PayloadS3Pointer.class)); - } catch (Exception e) { - LOG.error("Failed to read the S3 object pointer from given string.", e); - return empty(); - } - } - - public Optional toJson() { - try { - ObjectWriter objectWriter = objectMapper.writer(); - return ofNullable(objectWriter.writeValueAsString(this)); - - } catch (Exception e) { - LOG.error("Failed to convert S3 object pointer to text.", e); - return empty(); - } - } -} From 31aec8e64dd353a0f5542169da25fb1e5cfd6db4 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Fri, 29 Apr 2022 06:42:36 +0200 Subject: [PATCH 0121/1008] chore:Prep release 1.12.2 (#852) * chore:prep release 1.12.2 * Update CHANGELOG.md Co-authored-by: pankajagrawal16 --- CHANGELOG.md | 7 +++++++ README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 23 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5662a4538..5974e922d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.12.2] - 2022-04-29 + +### Bug Fixes + +* **SQS Large message processing**: Classpath conflict on `PayloadS3Pointer` when consumer application depends on `payloadoffloading-common`, introduced in [v1.8.0](https://github.com/awslabs/aws-lambda-powertools-java/releases/tag/v1.8.0). ([#851](https://github.com/awslabs/aws-lambda-powertools-java/pull/851)) + + ## [1.12.1] - 2022-04-21 ### Bug Fixes diff --git a/README.md b/README.md index fd750a5b9..250c77b5b 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.12.1 + 1.12.2 software.amazon.lambda powertools-logging - 1.12.1 + 1.12.2 software.amazon.lambda powertools-metrics - 1.12.1 + 1.12.2 ... diff --git a/mkdocs.yml b/mkdocs.yml index ba7925c2d..76dfde42c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,7 +77,7 @@ extra_javascript: extra: powertools: - version: 1.12.1 + version: 1.12.2 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index 5e5bb3f9b..30e019b68 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.12.1 + 1.12.2 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index c68e02cb7..68cff9b3d 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 94fb75be5..a050d46c7 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index d14226cb0..bb35b46e2 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.12.1 + 1.12.2 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 5d1fce836..2c14b55f1 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 226f646cb..32776b315 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 196aa17e2..511f9c603 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 1748838ca..80dc6c8c0 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 76abe5a93..d1146acaa 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index f61241022..9668727d5 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index e39780ce4..b59d99814 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 924c61cfe..16a8424d4 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.1 + 1.12.2 AWS Lambda Powertools for Java validation library From 3abefb4a3739376a3ba8f8157776faedd6b735ee Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Fri, 29 Apr 2022 08:18:14 +0200 Subject: [PATCH 0122/1008] chore(ci): upgrade to checkout v3 --- .github/workflows/auto-merge.yml | 2 +- .github/workflows/build-docs.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/release-prep.yml | 2 +- .github/workflows/spotbugs.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index f3cecfad4..198ca5826 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -11,7 +11,7 @@ jobs: 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@v2 + - uses: actions/checkout@v3 - uses: ahmadnassri/action-workflow-run-wait@v1 with: timeout: 300000 diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 1cd9446be..7412255d3 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -16,7 +16,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v1 with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eea26d905..66ea80739 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: JAVA: ${{ matrix.java-version }} AWS_REGION: eu-west-1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup java uses: actions/setup-java@v2 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 69aef263c..1e8e72aea 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,7 +10,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v1 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3f8d49a2d..a51530d26 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,7 +8,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Maven Central Repository uses: actions/setup-java@v2 with: diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml index a2f8c0817..b2cf656ba 100644 --- a/.github/workflows/release-prep.yml +++ b/.github/workflows/release-prep.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get current date id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 7955f4533..25dced078 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -22,7 +22,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup java JDK 1.8 uses: actions/setup-java@v2 with: From b6af95dab40482df7e947423d2bd11e6ca285d2f Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Fri, 29 Apr 2022 08:28:43 +0200 Subject: [PATCH 0123/1008] chore(ci): Address GitHub workaround for CVE-2022-24765. - apply additional work around stated in actions/checkout#766 --- .github/workflows/docs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1e8e72aea..9b2771d1e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,9 @@ jobs: 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 + run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" + make build-docs-website - name: Deploy all docs uses: peaceiris/actions-gh-pages@v3 with: From f702c1cab000d68179fe57f0378555609d045dc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:11:41 +0200 Subject: [PATCH 0124/1008] build(deps): bump aws.sdk.version from 2.17.179 to 2.17.180 (#854) Bumps `aws.sdk.version` from 2.17.179 to 2.17.180. Updates `software.amazon.awssdk:bom` from 2.17.179 to 2.17.180 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.179...2.17.180) Updates `http-client-spi` from 2.17.179 to 2.17.180 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.179...2.17.180) Updates `url-connection-client` from 2.17.179 to 2.17.180 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 30e019b68..855b68a49 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.179 + 2.17.180 2.11.1 2.1.2 UTF-8 From 3b8e41c2d1b0841f001b4dea70f4365890ff0f48 Mon Sep 17 00:00:00 2001 From: Pankaj Agrawal Date: Fri, 29 Apr 2022 14:15:13 +0200 Subject: [PATCH 0125/1008] chore(ci): fix build (#853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(ci): fix build * Update mkdocs.yml Removing git-revision-date plugin as it performs a git command that fails (https://github.com/actions/checkout/issues/766). * chore(ci): fix build * chore(ci): remove unused config Co-authored-by: Jérôme Van Der Linden --- .github/workflows/docs.yml | 1 - mkdocs.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9b2771d1e..d00f64e91 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,7 +22,6 @@ jobs: echo "SOURCE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - name: Build docs website run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" make build-docs-website - name: Deploy all docs uses: peaceiris/actions-gh-pages@v3 diff --git a/mkdocs.yml b/mkdocs.yml index 76dfde42c..e48370295 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,7 +65,6 @@ markdown_extensions: copyright: Copyright © 2021 Amazon Web Services plugins: - - git-revision-date - search - macros From 919dc5f654a7b534f534ed6d73865452b66bbe30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 13:11:41 +0200 Subject: [PATCH 0126/1008] build(deps): bump aws.sdk.version from 2.17.180 to 2.17.181 (#856) Bumps `aws.sdk.version` from 2.17.180 to 2.17.181. Updates `software.amazon.awssdk:bom` from 2.17.180 to 2.17.181 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.180...2.17.181) Updates `http-client-spi` from 2.17.180 to 2.17.181 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.180...2.17.181) Updates `url-connection-client` from 2.17.180 to 2.17.181 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 855b68a49..0b182f65b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.180 + 2.17.181 2.11.1 2.1.2 UTF-8 From c266556f8bc4629419d2c08d5437bd6685089a93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 May 2022 13:10:47 +0200 Subject: [PATCH 0127/1008] build(deps): bump aws.sdk.version from 2.17.181 to 2.17.182 (#857) Bumps `aws.sdk.version` from 2.17.181 to 2.17.182. Updates `software.amazon.awssdk:bom` from 2.17.181 to 2.17.182 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.181...2.17.182) Updates `http-client-spi` from 2.17.181 to 2.17.182 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.181...2.17.182) Updates `url-connection-client` from 2.17.181 to 2.17.182 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b182f65b..f6cdb6505 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.181 + 2.17.182 2.11.1 2.1.2 UTF-8 From 140ab759d0ef0c144edeaebdd5cea8784cc42c7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 13:11:28 +0200 Subject: [PATCH 0128/1008] build(deps): bump aws.sdk.version from 2.17.182 to 2.17.183 (#858) Bumps `aws.sdk.version` from 2.17.182 to 2.17.183. Updates `software.amazon.awssdk:bom` from 2.17.182 to 2.17.183 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.182...2.17.183) Updates `http-client-spi` from 2.17.182 to 2.17.183 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.182...2.17.183) Updates `url-connection-client` from 2.17.182 to 2.17.183 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f6cdb6505..b7c4c03aa 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.182 + 2.17.183 2.11.1 2.1.2 UTF-8 From 0c4916b2b76a0716c3eaffc9bcdc34e6bad1fb03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 May 2022 13:09:37 +0200 Subject: [PATCH 0129/1008] build(deps): bump aws.sdk.version from 2.17.183 to 2.17.184 (#859) Bumps `aws.sdk.version` from 2.17.183 to 2.17.184. Updates `software.amazon.awssdk:bom` from 2.17.183 to 2.17.184 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.183...2.17.184) Updates `http-client-spi` from 2.17.183 to 2.17.184 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.183...2.17.184) Updates `url-connection-client` from 2.17.183 to 2.17.184 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b7c4c03aa..8ec6bd62c 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.183 + 2.17.184 2.11.1 2.1.2 UTF-8 From f372c42d56e0727bacd5c1f57e623239ae41abbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 13:11:40 +0200 Subject: [PATCH 0130/1008] build(deps): bump aws.sdk.version from 2.17.184 to 2.17.185 (#860) Bumps `aws.sdk.version` from 2.17.184 to 2.17.185. Updates `software.amazon.awssdk:bom` from 2.17.184 to 2.17.185 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.184...2.17.185) Updates `http-client-spi` from 2.17.184 to 2.17.185 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.184...2.17.185) Updates `url-connection-client` from 2.17.184 to 2.17.185 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8ec6bd62c..e9d4a7395 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.184 + 2.17.185 2.11.1 2.1.2 UTF-8 From 3f61332792eebe7bbe24ec6e56a40f2857f1b08c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 13:13:28 +0200 Subject: [PATCH 0131/1008] build(deps): bump aws.sdk.version from 2.17.185 to 2.17.186 (#861) Bumps `aws.sdk.version` from 2.17.185 to 2.17.186. Updates `software.amazon.awssdk:bom` from 2.17.185 to 2.17.186 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.185...2.17.186) Updates `http-client-spi` from 2.17.185 to 2.17.186 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.185...2.17.186) Updates `url-connection-client` from 2.17.185 to 2.17.186 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e9d4a7395..461715cdb 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.185 + 2.17.186 2.11.1 2.1.2 UTF-8 From 17626792bccb90839762e6369e5a389431fe9eff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 May 2022 13:12:34 +0200 Subject: [PATCH 0132/1008] build(deps): bump aws.sdk.version from 2.17.186 to 2.17.187 (#862) Bumps `aws.sdk.version` from 2.17.186 to 2.17.187. Updates `software.amazon.awssdk:bom` from 2.17.186 to 2.17.187 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.186...2.17.187) Updates `http-client-spi` from 2.17.186 to 2.17.187 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.186...2.17.187) Updates `url-connection-client` from 2.17.186 to 2.17.187 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 461715cdb..fab786baa 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.186 + 2.17.187 2.11.1 2.1.2 UTF-8 From c4d0c1e7ee502c3fc6a018140cb8229adeaee5f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 May 2022 13:11:51 +0200 Subject: [PATCH 0133/1008] build(deps): bump aws.sdk.version from 2.17.187 to 2.17.188 (#863) Bumps `aws.sdk.version` from 2.17.187 to 2.17.188. Updates `software.amazon.awssdk:bom` from 2.17.187 to 2.17.188 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.187...2.17.188) Updates `http-client-spi` from 2.17.187 to 2.17.188 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.187...2.17.188) Updates `url-connection-client` from 2.17.187 to 2.17.188 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fab786baa..401438903 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.187 + 2.17.188 2.11.1 2.1.2 UTF-8 From 15e579367472de9d831871d220227dc03ca6a936 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 May 2022 13:11:33 +0200 Subject: [PATCH 0134/1008] build(deps): bump aws.sdk.version from 2.17.188 to 2.17.189 (#864) Bumps `aws.sdk.version` from 2.17.188 to 2.17.189. Updates `software.amazon.awssdk:bom` from 2.17.188 to 2.17.189 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.188...2.17.189) Updates `http-client-spi` from 2.17.188 to 2.17.189 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.188...2.17.189) Updates `url-connection-client` from 2.17.188 to 2.17.189 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 401438903..ed4395d6e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.188 + 2.17.189 2.11.1 2.1.2 UTF-8 From 31ff80ff0de3292fcf8454db2b7baf6885573c8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 May 2022 13:10:49 +0200 Subject: [PATCH 0135/1008] build(deps): bump aws.sdk.version from 2.17.189 to 2.17.190 (#866) Bumps `aws.sdk.version` from 2.17.189 to 2.17.190. Updates `software.amazon.awssdk:bom` from 2.17.189 to 2.17.190 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.189...2.17.190) Updates `http-client-spi` from 2.17.189 to 2.17.190 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.189...2.17.190) Updates `url-connection-client` from 2.17.189 to 2.17.190 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed4395d6e..3dc899956 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.189 + 2.17.190 2.11.1 2.1.2 UTF-8 From 895ec8208410f486bbf5b105c3b00d2ec364f5da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 13:10:57 +0200 Subject: [PATCH 0136/1008] build(deps): bump aws.sdk.version from 2.17.190 to 2.17.191 (#867) Bumps `aws.sdk.version` from 2.17.190 to 2.17.191. Updates `software.amazon.awssdk:bom` from 2.17.190 to 2.17.191 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.190...2.17.191) Updates `http-client-spi` from 2.17.190 to 2.17.191 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.190...2.17.191) Updates `url-connection-client` from 2.17.190 to 2.17.191 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3dc899956..52bebaa44 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.190 + 2.17.191 2.11.1 2.1.2 UTF-8 From 7e28cf1852a975255b3ca0bc73891999a0c532be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 13:10:56 +0200 Subject: [PATCH 0137/1008] build(deps): bump aws.sdk.version from 2.17.191 to 2.17.192 (#869) Bumps `aws.sdk.version` from 2.17.191 to 2.17.192. Updates `software.amazon.awssdk:bom` from 2.17.191 to 2.17.192 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.191...2.17.192) Updates `http-client-spi` from 2.17.191 to 2.17.192 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.191...2.17.192) Updates `url-connection-client` from 2.17.191 to 2.17.192 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 52bebaa44..6ea9d3996 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.191 + 2.17.192 2.11.1 2.1.2 UTF-8 From 2f768617a0626ee36339ea6fb334e8944d732e04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 May 2022 13:11:14 +0200 Subject: [PATCH 0138/1008] build(deps): bump aws.sdk.version from 2.17.192 to 2.17.193 (#870) Bumps `aws.sdk.version` from 2.17.192 to 2.17.193. Updates `software.amazon.awssdk:bom` from 2.17.192 to 2.17.193 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.192...2.17.193) Updates `http-client-spi` from 2.17.192 to 2.17.193 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.192...2.17.193) Updates `url-connection-client` from 2.17.192 to 2.17.193 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ea9d3996..a2895725f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.192 + 2.17.193 2.11.1 2.1.2 UTF-8 From d1fd4c507011c1fa87cfad896a338e04fedb3af8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 May 2022 13:11:21 +0200 Subject: [PATCH 0139/1008] build(deps): bump aws.sdk.version from 2.17.193 to 2.17.194 (#871) Bumps `aws.sdk.version` from 2.17.193 to 2.17.194. Updates `software.amazon.awssdk:bom` from 2.17.193 to 2.17.194 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.193...2.17.194) Updates `http-client-spi` from 2.17.193 to 2.17.194 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.193...2.17.194) Updates `url-connection-client` from 2.17.193 to 2.17.194 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a2895725f..4d24d3d19 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.193 + 2.17.194 2.11.1 2.1.2 UTF-8 From 3d5ea20b81c9baba8be9c2c7387525f6757536c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 May 2022 13:10:29 +0200 Subject: [PATCH 0140/1008] build(deps): bump aws.sdk.version from 2.17.194 to 2.17.195 (#872) Bumps `aws.sdk.version` from 2.17.194 to 2.17.195. Updates `software.amazon.awssdk:bom` from 2.17.194 to 2.17.195 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.194...2.17.195) Updates `http-client-spi` from 2.17.194 to 2.17.195 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.194...2.17.195) Updates `url-connection-client` from 2.17.194 to 2.17.195 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4d24d3d19..61164ec57 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.194 + 2.17.195 2.11.1 2.1.2 UTF-8 From f03a3d7ae4d88f396e0025078b15497493b8cda7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 May 2022 13:11:02 +0200 Subject: [PATCH 0141/1008] build(deps): bump aws.sdk.version from 2.17.195 to 2.17.196 (#874) Bumps `aws.sdk.version` from 2.17.195 to 2.17.196. Updates `software.amazon.awssdk:bom` from 2.17.195 to 2.17.196 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.195...2.17.196) Updates `http-client-spi` from 2.17.195 to 2.17.196 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.195...2.17.196) Updates `url-connection-client` from 2.17.195 to 2.17.196 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61164ec57..46c9c75bd 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.195 + 2.17.196 2.11.1 2.1.2 UTF-8 From dce21e05082ab4e6b78313d29e7723b62f44332f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 13:11:16 +0200 Subject: [PATCH 0142/1008] build(deps): bump json-schema-validator from 1.0.69 to 1.0.70 (#876) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.69 to 1.0.70. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.69...1.0.70) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 16a8424d4..eacd0ac5c 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.69 + 1.0.70 From cb6f46a0c1536c49a38e07c4a5faf76aff390219 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 13:12:09 +0200 Subject: [PATCH 0143/1008] build(deps): bump aws.sdk.version from 2.17.196 to 2.17.198 (#877) Bumps `aws.sdk.version` from 2.17.196 to 2.17.198. Updates `software.amazon.awssdk:bom` from 2.17.196 to 2.17.198 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.196...2.17.198) Updates `http-client-spi` from 2.17.196 to 2.17.198 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.196...2.17.198) Updates `url-connection-client` from 2.17.196 to 2.17.198 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46c9c75bd..617d16931 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.196 + 2.17.198 2.11.1 2.1.2 UTF-8 From 97709fb52dde0b8a70c1cd6256843211a8b9b362 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 May 2022 13:11:26 +0200 Subject: [PATCH 0144/1008] build(deps): bump aws.sdk.version from 2.17.198 to 2.17.199 (#879) Bumps `aws.sdk.version` from 2.17.198 to 2.17.199. Updates `software.amazon.awssdk:bom` from 2.17.198 to 2.17.199 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.198...2.17.199) Updates `http-client-spi` from 2.17.198 to 2.17.199 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.198...2.17.199) Updates `url-connection-client` from 2.17.198 to 2.17.199 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 617d16931..dbcfde1c2 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.2.2 1.9.7 - 2.17.198 + 2.17.199 2.11.1 2.1.2 UTF-8 From 6f506991b7c620ed55774b34c7f12ff69dc6b044 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 10:34:19 +0200 Subject: [PATCH 0145/1008] build(deps): bump jackson-databind from 2.13.2.2 to 2.13.3 (#868) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.2.2 to 2.13.3. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dbcfde1c2..0c7285bbf 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.17.2 - 2.13.2.2 + 2.13.3 1.9.7 2.17.199 2.11.1 From ba1b76168d45084e72ced03082377d915a2b658c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 10:34:57 +0200 Subject: [PATCH 0146/1008] build(deps-dev): bump junit-pioneer from 1.7.0 to 1.7.1 (#878) Bumps [junit-pioneer](https://github.com/junit-pioneer/junit-pioneer) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/junit-pioneer/junit-pioneer/releases) - [Commits](https://github.com/junit-pioneer/junit-pioneer/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: org.junit-pioneer:junit-pioneer dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index bb35b46e2..68b40082d 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -91,7 +91,7 @@ org.junit-pioneer junit-pioneer - 1.7.0 + 1.7.1 test From c47b158631df3b5447e404695213a5308405f6a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 13:10:27 +0200 Subject: [PATCH 0147/1008] build(deps): bump mockito-inline from 4.5.1 to 4.6.0 (#880) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.5.1 to 4.6.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.5.1...v4.6.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c7285bbf..b1ce87bf5 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.5.1 + 4.6.0 test From 809ee6ed0d9edc87250d249caef3fe8c0bca00ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 13:55:17 +0200 Subject: [PATCH 0148/1008] build(deps): bump aws.sdk.version from 2.17.199 to 2.17.201 (#882) Bumps `aws.sdk.version` from 2.17.199 to 2.17.201. Updates `software.amazon.awssdk:bom` from 2.17.199 to 2.17.201 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.199...2.17.201) Updates `http-client-spi` from 2.17.199 to 2.17.201 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.199...2.17.201) Updates `url-connection-client` from 2.17.199 to 2.17.201 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b1ce87bf5..c742d072e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.199 + 2.17.201 2.11.1 2.1.2 UTF-8 From 810c8e40ee6e57d7a75334f9108b9cf26ff20999 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 13:55:41 +0200 Subject: [PATCH 0149/1008] build(deps): bump mockito-core from 4.5.1 to 4.6.0 (#881) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.5.1 to 4.6.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.5.1...v4.6.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c742d072e..68a46f237 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.5.1 + 4.6.0 test From 1deffbb685351d51503b8cf23b297d6931421d67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:11:23 +0200 Subject: [PATCH 0150/1008] build(deps): bump assertj-core from 3.22.0 to 3.23.0 (#883) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 68a46f237..b8fbf6878 100644 --- a/pom.xml +++ b/pom.xml @@ -262,7 +262,7 @@ org.assertj assertj-core - 3.22.0 + 3.23.0 test From b8ea407a54ed7126a6527b51585df71197b20879 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:45:50 +0200 Subject: [PATCH 0151/1008] build(deps): bump aws.sdk.version from 2.17.201 to 2.17.202 (#884) Bumps `aws.sdk.version` from 2.17.201 to 2.17.202. Updates `software.amazon.awssdk:bom` from 2.17.201 to 2.17.202 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.201...2.17.202) Updates `http-client-spi` from 2.17.201 to 2.17.202 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.201...2.17.202) Updates `url-connection-client` from 2.17.201 to 2.17.202 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b8fbf6878..b73782f75 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.201 + 2.17.202 2.11.1 2.1.2 UTF-8 From 34b39c94ee34831c785e9f57a355f000518854a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jun 2022 13:11:10 +0200 Subject: [PATCH 0152/1008] build(deps): bump aws.sdk.version from 2.17.202 to 2.17.203 (#886) Bumps `aws.sdk.version` from 2.17.202 to 2.17.203. Updates `software.amazon.awssdk:bom` from 2.17.202 to 2.17.203 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.202...2.17.203) Updates `http-client-spi` from 2.17.202 to 2.17.203 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.202...2.17.203) Updates `url-connection-client` from 2.17.202 to 2.17.203 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b73782f75..da00cf44a 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.202 + 2.17.203 2.11.1 2.1.2 UTF-8 From 766eec2d14eba9b5aebcc3b3601a18714756d357 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jun 2022 13:12:50 +0200 Subject: [PATCH 0153/1008] build(deps): bump mockito-inline from 4.6.0 to 4.6.1 (#888) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.0...v4.6.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da00cf44a..bf0e891e5 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.6.0 + 4.6.1 test From bf6dc6830e4e9e8d4d8ab5dfd85074ca9ca14483 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 13:11:50 +0200 Subject: [PATCH 0154/1008] build(deps): bump aws.xray.recorder.version from 2.11.1 to 2.11.2 (#891) Bumps `aws.xray.recorder.version` from 2.11.1 to 2.11.2. Updates `aws-xray-recorder-sdk-core` from 2.11.1 to 2.11.2 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.1...v2.11.2) Updates `aws-xray-recorder-sdk-aws-sdk-core` from 2.11.1 to 2.11.2 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.1...v2.11.2) Updates `aws-xray-recorder-sdk-aws-sdk-v2` from 2.11.1 to 2.11.2 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.1...v2.11.2) Updates `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.11.1 to 2.11.2 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.1...v2.11.2) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf0e891e5..f09c20298 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 2.13.3 1.9.7 2.17.203 - 2.11.1 + 2.11.2 2.1.2 UTF-8 1.2.1 From 74bb79be4d139629faf198f4b6ac01fd51f46bf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 13:20:51 +0200 Subject: [PATCH 0155/1008] build(deps): bump aws.sdk.version from 2.17.203 to 2.17.205 (#890) Bumps `aws.sdk.version` from 2.17.203 to 2.17.205. Updates `software.amazon.awssdk:bom` from 2.17.203 to 2.17.205 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.203...2.17.205) Updates `http-client-spi` from 2.17.203 to 2.17.205 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.203...2.17.205) Updates `url-connection-client` from 2.17.203 to 2.17.205 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f09c20298..9dc1cb62e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.203 + 2.17.205 2.11.2 2.1.2 UTF-8 From ed811714ab7832a22fb163ec7024296bd40c489b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 13:10:39 +0200 Subject: [PATCH 0156/1008] build(deps): bump aws.sdk.version from 2.17.205 to 2.17.206 (#892) Bumps `aws.sdk.version` from 2.17.205 to 2.17.206. Updates `software.amazon.awssdk:bom` from 2.17.205 to 2.17.206 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.205...2.17.206) Updates `http-client-spi` from 2.17.205 to 2.17.206 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.205...2.17.206) Updates `url-connection-client` from 2.17.205 to 2.17.206 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9dc1cb62e..37ecf373b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.205 + 2.17.206 2.11.2 2.1.2 UTF-8 From 8cb999563838028b8a5811222d61a574cbe6696c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 13:10:54 +0200 Subject: [PATCH 0157/1008] build(deps): bump aws.sdk.version from 2.17.206 to 2.17.207 (#893) Bumps `aws.sdk.version` from 2.17.206 to 2.17.207. Updates `software.amazon.awssdk:bom` from 2.17.206 to 2.17.207 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.206...2.17.207) Updates `http-client-spi` from 2.17.206 to 2.17.207 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.206...2.17.207) Updates `url-connection-client` from 2.17.206 to 2.17.207 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 37ecf373b..7c73169d2 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.206 + 2.17.207 2.11.2 2.1.2 UTF-8 From fb43e12335cc9ed00f578e739072cc42883eff02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:12:32 +0200 Subject: [PATCH 0158/1008] build(deps): bump aws.sdk.version from 2.17.207 to 2.17.208 (#894) Bumps `aws.sdk.version` from 2.17.207 to 2.17.208. Updates `software.amazon.awssdk:bom` from 2.17.207 to 2.17.208 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.207...2.17.208) Updates `http-client-spi` from 2.17.207 to 2.17.208 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.207...2.17.208) Updates `url-connection-client` from 2.17.207 to 2.17.208 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c73169d2..782d954c5 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.207 + 2.17.208 2.11.2 2.1.2 UTF-8 From ff1c5f7769f91c38c2290748d74a27f72ab2e757 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:11:35 +0200 Subject: [PATCH 0159/1008] build(deps): bump aws.sdk.version from 2.17.208 to 2.17.209 (#895) Bumps `aws.sdk.version` from 2.17.208 to 2.17.209. Updates `software.amazon.awssdk:bom` from 2.17.208 to 2.17.209 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.208...2.17.209) Updates `http-client-spi` from 2.17.208 to 2.17.209 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.208...2.17.209) Updates `url-connection-client` from 2.17.208 to 2.17.209 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 782d954c5..427fe3178 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.208 + 2.17.209 2.11.2 2.1.2 UTF-8 From e37d555533eb5cf53ef6bc481eb3ee9fa5742841 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 13:12:25 +0200 Subject: [PATCH 0160/1008] build(deps): bump aws.sdk.version from 2.17.209 to 2.17.210 (#896) Bumps `aws.sdk.version` from 2.17.209 to 2.17.210. Updates `software.amazon.awssdk:bom` from 2.17.209 to 2.17.210 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.209...2.17.210) Updates `http-client-spi` from 2.17.209 to 2.17.210 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.209...2.17.210) Updates `url-connection-client` from 2.17.209 to 2.17.210 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 427fe3178..e4634b0e5 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.209 + 2.17.210 2.11.2 2.1.2 UTF-8 From bb75530ab75fa84ad0f2f3b38bac794ed894ed49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:11:25 +0200 Subject: [PATCH 0161/1008] build(deps): bump aws.sdk.version from 2.17.210 to 2.17.211 (#897) Bumps `aws.sdk.version` from 2.17.210 to 2.17.211. Updates `software.amazon.awssdk:bom` from 2.17.210 to 2.17.211 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.210...2.17.211) Updates `http-client-spi` from 2.17.210 to 2.17.211 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.210...2.17.211) Updates `url-connection-client` from 2.17.210 to 2.17.211 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4634b0e5..71267e92b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.210 + 2.17.211 2.11.2 2.1.2 UTF-8 From 235b07c591eafca99300e3111cf45e3557c34349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jun 2022 13:12:11 +0200 Subject: [PATCH 0162/1008] build(deps): bump aws.sdk.version from 2.17.211 to 2.17.212 (#898) Bumps `aws.sdk.version` from 2.17.211 to 2.17.212. Updates `software.amazon.awssdk:bom` from 2.17.211 to 2.17.212 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.211...2.17.212) Updates `http-client-spi` from 2.17.211 to 2.17.212 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.211...2.17.212) Updates `url-connection-client` from 2.17.211 to 2.17.212 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71267e92b..0497f37d2 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.211 + 2.17.212 2.11.2 2.1.2 UTF-8 From 860d215690320423e72ad3d089b1e514677b80cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jun 2022 13:11:25 +0200 Subject: [PATCH 0163/1008] build(deps): bump aws.sdk.version from 2.17.212 to 2.17.213 (#900) Bumps `aws.sdk.version` from 2.17.212 to 2.17.213. Updates `software.amazon.awssdk:bom` from 2.17.212 to 2.17.213 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.212...2.17.213) Updates `http-client-spi` from 2.17.212 to 2.17.213 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.212...2.17.213) Updates `url-connection-client` from 2.17.212 to 2.17.213 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0497f37d2..56bd28bb6 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.212 + 2.17.213 2.11.2 2.1.2 UTF-8 From 57218b21127f1fb41557970c2e47f694c4256248 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 13:14:37 +0200 Subject: [PATCH 0164/1008] build(deps): bump aws.sdk.version from 2.17.213 to 2.17.214 (#901) Bumps `aws.sdk.version` from 2.17.213 to 2.17.214. Updates `software.amazon.awssdk:bom` from 2.17.213 to 2.17.214 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.213...2.17.214) Updates `http-client-spi` from 2.17.213 to 2.17.214 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.213...2.17.214) Updates `url-connection-client` from 2.17.213 to 2.17.214 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56bd28bb6..d1c0b910d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.213 + 2.17.214 2.11.2 2.1.2 UTF-8 From d53ea7e4738795113223480a029f01c85ba88be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:14:21 +0200 Subject: [PATCH 0165/1008] build(deps): bump aws.sdk.version from 2.17.214 to 2.17.215 (#902) Bumps `aws.sdk.version` from 2.17.214 to 2.17.215. Updates `software.amazon.awssdk:bom` from 2.17.214 to 2.17.215 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.214...2.17.215) Updates `http-client-spi` from 2.17.214 to 2.17.215 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.214...2.17.215) Updates `url-connection-client` from 2.17.214 to 2.17.215 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d1c0b910d..23a33ada0 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.214 + 2.17.215 2.11.2 2.1.2 UTF-8 From be3d6334f56bfd1f3405dfe6247f691cd1995639 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jun 2022 13:16:31 +0200 Subject: [PATCH 0166/1008] build(deps): bump aws.sdk.version from 2.17.215 to 2.17.216 (#903) Bumps `aws.sdk.version` from 2.17.215 to 2.17.216. Updates `software.amazon.awssdk:bom` from 2.17.215 to 2.17.216 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.215...2.17.216) Updates `http-client-spi` from 2.17.215 to 2.17.216 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.215...2.17.216) Updates `url-connection-client` from 2.17.215 to 2.17.216 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 23a33ada0..a128420dc 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.215 + 2.17.216 2.11.2 2.1.2 UTF-8 From ce605c6d43e953274cca775aa7d712793201ff18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:50:24 +0200 Subject: [PATCH 0167/1008] build(deps): bump aws.sdk.version from 2.17.216 to 2.17.217 (#904) Bumps `aws.sdk.version` from 2.17.216 to 2.17.217. Updates `software.amazon.awssdk:bom` from 2.17.216 to 2.17.217 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.216...2.17.217) Updates `http-client-spi` from 2.17.216 to 2.17.217 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.216...2.17.217) Updates `url-connection-client` from 2.17.216 to 2.17.217 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a128420dc..89ce7bc02 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.216 + 2.17.217 2.11.2 2.1.2 UTF-8 From 983615eb4ac58814b3164e0a24a26226c07624a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jun 2022 13:11:05 +0200 Subject: [PATCH 0168/1008] build(deps): bump aws.sdk.version from 2.17.217 to 2.17.218 (#905) Bumps `aws.sdk.version` from 2.17.217 to 2.17.218. Updates `software.amazon.awssdk:bom` from 2.17.217 to 2.17.218 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.217...2.17.218) Updates `http-client-spi` from 2.17.217 to 2.17.218 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.217...2.17.218) Updates `url-connection-client` from 2.17.217 to 2.17.218 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 89ce7bc02..176ae5ac8 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.217 + 2.17.218 2.11.2 2.1.2 UTF-8 From 90c264ce2b1cdca7b832a189798071ce9dab5bee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jun 2022 13:11:24 +0200 Subject: [PATCH 0169/1008] build(deps): bump aws.sdk.version from 2.17.218 to 2.17.219 (#906) Bumps `aws.sdk.version` from 2.17.218 to 2.17.219. Updates `software.amazon.awssdk:bom` from 2.17.218 to 2.17.219 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.218...2.17.219) Updates `http-client-spi` from 2.17.218 to 2.17.219 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.218...2.17.219) Updates `url-connection-client` from 2.17.218 to 2.17.219 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 176ae5ac8..8bb5f2a2f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.218 + 2.17.219 2.11.2 2.1.2 UTF-8 From 0756024d99cd4dfd2157f6cf33f0e5ef5b7b6a7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 13:16:05 +0200 Subject: [PATCH 0170/1008] build(deps): bump aws.sdk.version from 2.17.219 to 2.17.220 (#907) Bumps `aws.sdk.version` from 2.17.219 to 2.17.220. Updates `software.amazon.awssdk:bom` from 2.17.219 to 2.17.220 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.219...2.17.220) Updates `http-client-spi` from 2.17.219 to 2.17.220 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.219...2.17.220) Updates `url-connection-client` from 2.17.219 to 2.17.220 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8bb5f2a2f..ed93c8e6e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.219 + 2.17.220 2.11.2 2.1.2 UTF-8 From 12075d05b3ce770b0a7cd291d964dfab43b03e2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:11:26 +0200 Subject: [PATCH 0171/1008] build(deps): bump aws.sdk.version from 2.17.220 to 2.17.221 (#908) Bumps `aws.sdk.version` from 2.17.220 to 2.17.221. Updates `software.amazon.awssdk:bom` from 2.17.220 to 2.17.221 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.220...2.17.221) Updates `http-client-spi` from 2.17.220 to 2.17.221 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.220...2.17.221) Updates `url-connection-client` from 2.17.220 to 2.17.221 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed93c8e6e..93e5559e7 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.220 + 2.17.221 2.11.2 2.1.2 UTF-8 From dad733874a4f100ef025fa2b6b5910ec72bc79ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:12:01 +0200 Subject: [PATCH 0172/1008] build(deps): bump aws.sdk.version from 2.17.221 to 2.17.222 (#909) Bumps `aws.sdk.version` from 2.17.221 to 2.17.222. Updates `software.amazon.awssdk:bom` from 2.17.221 to 2.17.222 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.221...2.17.222) Updates `http-client-spi` from 2.17.221 to 2.17.222 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.221...2.17.222) Updates `url-connection-client` from 2.17.221 to 2.17.222 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93e5559e7..484cfc394 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.221 + 2.17.222 2.11.2 2.1.2 UTF-8 From 0184106b997ba587a33a5ac09a669ad33c3aa6b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 13:17:01 +0200 Subject: [PATCH 0173/1008] build(deps): bump aws.sdk.version from 2.17.222 to 2.17.223 (#910) Bumps `aws.sdk.version` from 2.17.222 to 2.17.223. Updates `software.amazon.awssdk:bom` from 2.17.222 to 2.17.223 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.222...2.17.223) Updates `http-client-spi` from 2.17.222 to 2.17.223 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.222...2.17.223) Updates `url-connection-client` from 2.17.222 to 2.17.223 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 484cfc394..543bf004b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.222 + 2.17.223 2.11.2 2.1.2 UTF-8 From f0fd527214d87ebbe203180c969bdc8966b274ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 13:12:56 +0200 Subject: [PATCH 0174/1008] build(deps): bump aws.sdk.version from 2.17.223 to 2.17.224 (#911) Bumps `aws.sdk.version` from 2.17.223 to 2.17.224. Updates `software.amazon.awssdk:bom` from 2.17.223 to 2.17.224 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.223...2.17.224) Updates `http-client-spi` from 2.17.223 to 2.17.224 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.223...2.17.224) Updates `url-connection-client` from 2.17.223 to 2.17.224 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 543bf004b..8a6abfa0d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.17.2 2.13.3 1.9.7 - 2.17.223 + 2.17.224 2.11.2 2.1.2 UTF-8 From 6d9050a5ae4b7737a5bf4d7c0cfae34d25b3985e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 13:26:18 +0200 Subject: [PATCH 0175/1008] build(deps): bump log4j.version from 2.17.2 to 2.18.0 (#912) Bumps `log4j.version` from 2.17.2 to 2.18.0. Updates `log4j-core` from 2.17.2 to 2.18.0 Updates `log4j-slf4j-impl` from 2.17.2 to 2.18.0 Updates `log4j-api` from 2.17.2 to 2.18.0 Updates `log4j-layout-template-json` from 2.17.2 to 2.18.0 Updates `log4j-jcl` from 2.17.2 to 2.18.0 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8a6abfa0d..6a17dba63 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 1.8 1.8 - 2.17.2 + 2.18.0 2.13.3 1.9.7 2.17.224 From 80a5371f312b14396c0f4f58f2ddbf7f35021e7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:11:37 +0200 Subject: [PATCH 0176/1008] build(deps): bump jsonassert from 1.5.0 to 1.5.1 (#913) Bumps [jsonassert](https://github.com/skyscreamer/JSONassert) from 1.5.0 to 1.5.1. - [Release notes](https://github.com/skyscreamer/JSONassert/releases) - [Changelog](https://github.com/skyscreamer/JSONassert/blob/master/CHANGELOG.md) - [Commits](https://github.com/skyscreamer/JSONassert/compare/jsonassert-1.5.0...jsonassert-1.5.1) --- updated-dependencies: - dependency-name: org.skyscreamer:jsonassert dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a17dba63..3a4775398 100644 --- a/pom.xml +++ b/pom.xml @@ -268,7 +268,7 @@ org.skyscreamer jsonassert - 1.5.0 + 1.5.1 test From 4ff03b5a62b1fd13489a7d56cd2a6901a12db376 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Jul 2022 13:18:58 +0200 Subject: [PATCH 0177/1008] build(deps): bump aws.sdk.version from 2.17.224 to 2.17.225 (#914) Bumps `aws.sdk.version` from 2.17.224 to 2.17.225. Updates `software.amazon.awssdk:bom` from 2.17.224 to 2.17.225 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.224...2.17.225) Updates `http-client-spi` from 2.17.224 to 2.17.225 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.224...2.17.225) Updates `url-connection-client` from 2.17.224 to 2.17.225 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3a4775398..92bcd6013 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.224 + 2.17.225 2.11.2 2.1.2 UTF-8 From f57b63f2b77d77e64f59289e6df98dd00ac735b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Jul 2022 13:12:00 +0200 Subject: [PATCH 0178/1008] build(deps): bump aws.sdk.version from 2.17.225 to 2.17.226 (#915) Bumps `aws.sdk.version` from 2.17.225 to 2.17.226. Updates `software.amazon.awssdk:bom` from 2.17.225 to 2.17.226 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.225...2.17.226) Updates `http-client-spi` from 2.17.225 to 2.17.226 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.225...2.17.226) Updates `url-connection-client` from 2.17.225 to 2.17.226 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 92bcd6013..50afc3ae3 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.225 + 2.17.226 2.11.2 2.1.2 UTF-8 From 2474199b13ff0e7d41c52f3ffea3e139ffa913f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Jul 2022 13:12:17 +0200 Subject: [PATCH 0179/1008] build(deps): bump aws.sdk.version from 2.17.226 to 2.17.227 (#916) Bumps `aws.sdk.version` from 2.17.226 to 2.17.227. Updates `software.amazon.awssdk:bom` from 2.17.226 to 2.17.227 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.226...2.17.227) Updates `http-client-spi` from 2.17.226 to 2.17.227 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.226...2.17.227) Updates `url-connection-client` from 2.17.226 to 2.17.227 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50afc3ae3..dcd46a6d7 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.226 + 2.17.227 2.11.2 2.1.2 UTF-8 From fdda146c97840ae9ca9b3942bf5ad56ea92fd572 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jul 2022 13:13:28 +0200 Subject: [PATCH 0180/1008] build(deps): bump aws.sdk.version from 2.17.227 to 2.17.228 (#917) Bumps `aws.sdk.version` from 2.17.227 to 2.17.228. Updates `software.amazon.awssdk:bom` from 2.17.227 to 2.17.228 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.227...2.17.228) Updates `http-client-spi` from 2.17.227 to 2.17.228 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.227...2.17.228) Updates `url-connection-client` from 2.17.227 to 2.17.228 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dcd46a6d7..aca1e9b98 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.227 + 2.17.228 2.11.2 2.1.2 UTF-8 From 45c21630d43b31a68b8713e32d968cc90d4054ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:15:22 +0200 Subject: [PATCH 0181/1008] build(deps): bump aws.sdk.version from 2.17.228 to 2.17.229 (#918) Bumps `aws.sdk.version` from 2.17.228 to 2.17.229. Updates `software.amazon.awssdk:bom` from 2.17.228 to 2.17.229 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.228...2.17.229) Updates `http-client-spi` from 2.17.228 to 2.17.229 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.228...2.17.229) Updates `url-connection-client` from 2.17.228 to 2.17.229 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aca1e9b98..b5d6a330b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.228 + 2.17.229 2.11.2 2.1.2 UTF-8 From 804d8ea3542bf7b74a5b3a9e16f0bc5bd35eb197 Mon Sep 17 00:00:00 2001 From: Mark Sailes <45629314+msailes@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:49:40 +0100 Subject: [PATCH 0182/1008] chore:Prep release 1.12.3 (#920) --- CHANGELOG.md | 7 +++++++ README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 23 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5974e922d..005680a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.12.3] - 2022-07-12 + +### Maintenance + +* Fixes to resolve vulnerable transitive dependencies ([919](https://github.com/awslabs/aws-lambda-powertools-java/issues/919)) + + ## [1.12.2] - 2022-04-29 ### Bug Fixes diff --git a/README.md b/README.md index 250c77b5b..68827a999 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.12.2 + 1.12.3 software.amazon.lambda powertools-logging - 1.12.2 + 1.12.3 software.amazon.lambda powertools-metrics - 1.12.2 + 1.12.3 ... diff --git a/mkdocs.yml b/mkdocs.yml index e48370295..574b8b30a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -76,7 +76,7 @@ extra_javascript: extra: powertools: - version: 1.12.2 + version: 1.12.3 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index b5d6a330b..0f9cf2cf4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.12.2 + 1.12.3 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 68cff9b3d..2547e717e 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index a050d46c7..f54c24e12 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 68b40082d..7ddc1e793 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.12.2 + 1.12.3 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 2c14b55f1..59a98b4a9 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 32776b315..1f83a6c19 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 511f9c603..fb05bf738 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 80dc6c8c0..69176ea3a 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index d1146acaa..f3ebd5e58 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 9668727d5..95f9a8b22 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index b59d99814..59221ac4d 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index eacd0ac5c..80ad2ed14 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.2 + 1.12.3 AWS Lambda Powertools for Java validation library From 721fdad800ab52237132d8eee67a07269cd0fb4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:12:27 +0200 Subject: [PATCH 0183/1008] build(deps): bump aws.sdk.version from 2.17.229 to 2.17.230 (#921) Bumps `aws.sdk.version` from 2.17.229 to 2.17.230. Updates `software.amazon.awssdk:bom` from 2.17.229 to 2.17.230 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.229...2.17.230) Updates `http-client-spi` from 2.17.229 to 2.17.230 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.229...2.17.230) Updates `url-connection-client` from 2.17.229 to 2.17.230 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0f9cf2cf4..bf141e2ed 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.229 + 2.17.230 2.11.2 2.1.2 UTF-8 From 4eb23d28a7a3cb3b3cf8109e99ab91e9f173405f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Jul 2022 13:11:08 +0200 Subject: [PATCH 0184/1008] build(deps): bump aws.sdk.version from 2.17.230 to 2.17.231 (#922) Bumps `aws.sdk.version` from 2.17.230 to 2.17.231. Updates `software.amazon.awssdk:bom` from 2.17.230 to 2.17.231 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.230...2.17.231) Updates `http-client-spi` from 2.17.230 to 2.17.231 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.230...2.17.231) Updates `url-connection-client` from 2.17.230 to 2.17.231 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf141e2ed..199a05485 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.230 + 2.17.231 2.11.2 2.1.2 UTF-8 From 4c3e08f27ccd8f2cb6748418d44fecb0dc7d788b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:11:03 +0200 Subject: [PATCH 0185/1008] build(deps): bump aws.sdk.version from 2.17.231 to 2.17.232 (#923) Bumps `aws.sdk.version` from 2.17.231 to 2.17.232. Updates `software.amazon.awssdk:bom` from 2.17.231 to 2.17.232 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.231...2.17.232) Updates `http-client-spi` from 2.17.231 to 2.17.232 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.231...2.17.232) Updates `url-connection-client` from 2.17.231 to 2.17.232 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 199a05485..24a3f2380 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.231 + 2.17.232 2.11.2 2.1.2 UTF-8 From ab0f35620f2753d27a174c11d9155ae82ef1aceb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:16:34 +0200 Subject: [PATCH 0186/1008] build(deps): bump aws.sdk.version from 2.17.232 to 2.17.233 (#924) Bumps `aws.sdk.version` from 2.17.232 to 2.17.233. Updates `software.amazon.awssdk:bom` from 2.17.232 to 2.17.233 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.232...2.17.233) Updates `http-client-spi` from 2.17.232 to 2.17.233 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.232...2.17.233) Updates `url-connection-client` from 2.17.232 to 2.17.233 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 24a3f2380..e91e40148 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.232 + 2.17.233 2.11.2 2.1.2 UTF-8 From 765d5218c3b07b322172e626cfd72de60b404296 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Jul 2022 13:11:40 +0200 Subject: [PATCH 0187/1008] build(deps): bump aws.sdk.version from 2.17.233 to 2.17.234 (#926) Bumps `aws.sdk.version` from 2.17.233 to 2.17.234. Updates `software.amazon.awssdk:bom` from 2.17.233 to 2.17.234 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.233...2.17.234) Updates `http-client-spi` from 2.17.233 to 2.17.234 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.233...2.17.234) Updates `url-connection-client` from 2.17.233 to 2.17.234 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e91e40148..3b32ae350 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.233 + 2.17.234 2.11.2 2.1.2 UTF-8 From e18e7c04e1c0294c12632df0a86166551bc14335 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 13:13:28 +0200 Subject: [PATCH 0188/1008] build(deps): bump aws.sdk.version from 2.17.234 to 2.17.235 (#927) Bumps `aws.sdk.version` from 2.17.234 to 2.17.235. Updates `software.amazon.awssdk:bom` from 2.17.234 to 2.17.235 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.234...2.17.235) Updates `http-client-spi` from 2.17.234 to 2.17.235 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.234...2.17.235) Updates `url-connection-client` from 2.17.234 to 2.17.235 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b32ae350..60c76988d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.234 + 2.17.235 2.11.2 2.1.2 UTF-8 From cb579d46b45308c80cf69c29e64edae549115013 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 13:12:50 +0200 Subject: [PATCH 0189/1008] build(deps): bump aws.sdk.version from 2.17.235 to 2.17.236 (#928) Bumps `aws.sdk.version` from 2.17.235 to 2.17.236. Updates `software.amazon.awssdk:bom` from 2.17.235 to 2.17.236 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.235...2.17.236) Updates `http-client-spi` from 2.17.235 to 2.17.236 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.235...2.17.236) Updates `url-connection-client` from 2.17.235 to 2.17.236 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 60c76988d..1df1954c7 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.235 + 2.17.236 2.11.2 2.1.2 UTF-8 From 6644712fc0a173851a822bcac32e1e62689904f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 13:12:34 +0200 Subject: [PATCH 0190/1008] build(deps): bump aws.sdk.version from 2.17.236 to 2.17.237 (#929) Bumps `aws.sdk.version` from 2.17.236 to 2.17.237. Updates `software.amazon.awssdk:bom` from 2.17.236 to 2.17.237 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.236...2.17.237) Updates `http-client-spi` from 2.17.236 to 2.17.237 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.236...2.17.237) Updates `url-connection-client` from 2.17.236 to 2.17.237 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1df1954c7..43d4b04f5 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.236 + 2.17.237 2.11.2 2.1.2 UTF-8 From b57422150665902b175cb1c9307f3fe759fbdcdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 13:22:03 +0200 Subject: [PATCH 0191/1008] build(deps): bump aws.sdk.version from 2.17.237 to 2.17.238 (#930) Bumps `aws.sdk.version` from 2.17.237 to 2.17.238. Updates `software.amazon.awssdk:bom` from 2.17.237 to 2.17.238 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.237...2.17.238) Updates `http-client-spi` from 2.17.237 to 2.17.238 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.237...2.17.238) Updates `url-connection-client` from 2.17.237 to 2.17.238 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 43d4b04f5..fef15dbb4 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.237 + 2.17.238 2.11.2 2.1.2 UTF-8 From a2c33da2e81e6dd07b1b5fe37457acac0599ef2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Jul 2022 13:11:02 +0200 Subject: [PATCH 0192/1008] build(deps): bump aws.sdk.version from 2.17.238 to 2.17.239 (#931) Bumps `aws.sdk.version` from 2.17.238 to 2.17.239. Updates `software.amazon.awssdk:bom` from 2.17.238 to 2.17.239 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.238...2.17.239) Updates `http-client-spi` from 2.17.238 to 2.17.239 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.238...2.17.239) Updates `url-connection-client` from 2.17.238 to 2.17.239 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fef15dbb4..fc06bb8d5 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.18.0 2.13.3 1.9.7 - 2.17.238 + 2.17.239 2.11.2 2.1.2 UTF-8 From d3d4e0f14a5c43307144263558525d4058f282a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 13:12:14 +0200 Subject: [PATCH 0193/1008] build(deps): bump mockito-core from 4.6.0 to 4.7.0 (#934) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.0 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.0...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc06bb8d5..f3415d11d 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.6.0 + 4.7.0 test From 5f00186523cdf16a50dd72dbf4954f2aad8eae39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 13:21:10 +0200 Subject: [PATCH 0194/1008] build(deps): bump junit-jupiter.version from 5.8.2 to 5.9.0 (#935) Bumps `junit-jupiter.version` from 5.8.2 to 5.9.0. Updates `junit-jupiter-api` from 5.8.2 to 5.9.0 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) Updates `junit-jupiter-engine` from 5.8.2 to 5.9.0 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) Updates `junit-jupiter-params` from 5.8.2 to 5.9.0 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f3415d11d..1cd12f189 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ 3.4.0 3.2.1 3.0.1 - 5.8.2 + 5.9.0 1.0.6 0.5.1 From 1ba727ac93f73b193d36989dd7a91c81492933c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 13:17:44 +0200 Subject: [PATCH 0195/1008] build(deps): bump json-schema-validator from 1.0.70 to 1.0.73 (#937) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.70 to 1.0.73. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.70...1.0.73) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 80ad2ed14..61beb34ba 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.70 + 1.0.73 From d3ee0c4317690e6350b017758f468c8cac3068ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 13:26:10 +0200 Subject: [PATCH 0196/1008] build(deps): bump maven-javadoc-plugin from 3.4.0 to 3.4.1 (#938) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.0 to 3.4.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.0...maven-javadoc-plugin-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1cd12f189..08979af1d 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 0.8.8 2.7 1.6.8 - 3.4.0 + 3.4.1 3.2.1 3.0.1 5.9.0 From 935c57090f9818ad174bdc35a82828ad86544378 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 13:34:19 +0200 Subject: [PATCH 0197/1008] build(deps): bump maven-jar-plugin from 3.2.2 to 3.3.0 (#939) Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.2 to 3.3.0. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.2...maven-jar-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7ddc1e793..632619d45 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -170,7 +170,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.2 + 3.3.0 From 7702e41d6ccbf487c03475bd5664ff272f06a5b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 13:48:03 +0200 Subject: [PATCH 0198/1008] build(deps): bump log4j.version from 2.18.0 to 2.19.0 (#940) Bumps `log4j.version` from 2.18.0 to 2.19.0. Updates `log4j-core` from 2.18.0 to 2.19.0 Updates `log4j-slf4j-impl` from 2.18.0 to 2.19.0 Updates `log4j-api` from 2.18.0 to 2.19.0 Updates `log4j-layout-template-json` from 2.18.0 to 2.19.0 Updates `log4j-jcl` from 2.18.0 to 2.19.0 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 08979af1d..34a2a41e0 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 1.8 1.8 - 2.18.0 + 2.19.0 2.13.3 1.9.7 2.17.239 From 88d972fce590b7543305aae416a9d9314f51e1d5 Mon Sep 17 00:00:00 2001 From: Mark Sailes <45629314+msailes@users.noreply.github.com> Date: Wed, 12 Oct 2022 13:38:19 +0100 Subject: [PATCH 0199/1008] new form to allow customers self-nominate as public reference (#947) --- .github/ISSUE_TEMPLATE/support_powertools.yml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/support_powertools.yml diff --git a/.github/ISSUE_TEMPLATE/support_powertools.yml b/.github/ISSUE_TEMPLATE/support_powertools.yml new file mode 100644 index 000000000..362c07af5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_powertools.yml @@ -0,0 +1,64 @@ +name: Support Lambda Powertools (become a reference) +description: Add your organization's name or logo to the Lambda Powertools documentation +title: "[Support Lambda Powertools]: " +labels: ["customer_reference"] +body: + - type: markdown + attributes: + value: | + Thank you for becoming a reference customer. Your support means a lot to us. It also helps new customers to know who's using it. + + If you would like us to also display your organization's logo, please share a link in the `Company logo` field. + - type: input + id: organization + attributes: + label: Organization Name + description: Please share the name of your organization + placeholder: ACME + validations: + required: true + - type: input + id: name + attributes: + label: Your Name + description: Please share your name + validations: + required: true + - type: input + id: job + attributes: + label: Your current position + description: Please share your current position at your company + validations: + required: true + - type: input + id: logo + attributes: + label: (Optional) Company logo + description: Company logo you want us to display. You also allow us to resize for optimal placement in the documentation. + validations: + required: false + - type: textarea + id: use_case + attributes: + label: (Optional) Use case + description: How are you using Lambda Powertools today? *features, etc.* + validations: + required: false + - type: checkboxes + id: other_languages + attributes: + label: Also using other Lambda Powertools languages? + options: + - label: Python + required: false + - label: TypeScript + required: false + - label: .NET + required: false + - type: markdown + attributes: + value: | + *By raising a Support Lambda Powertools 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 From 69beaebf97378de6d25d4d4e19079d3492cd9051 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:11:07 +0200 Subject: [PATCH 0200/1008] build(deps): bump aws.sdk.version from 2.17.239 to 2.17.240 (#932) Bumps `aws.sdk.version` from 2.17.239 to 2.17.240. Updates `software.amazon.awssdk:bom` from 2.17.239 to 2.17.240 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.239...2.17.240) Updates `http-client-spi` from 2.17.239 to 2.17.240 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.239...2.17.240) Updates `url-connection-client` from 2.17.239 to 2.17.240 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 34a2a41e0..09bb28375 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.3 1.9.7 - 2.17.239 + 2.17.240 2.11.2 2.1.2 UTF-8 From a9484bd8e9b87d8a8b54b51760c64d6da72cc981 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:11:24 +0200 Subject: [PATCH 0201/1008] build(deps): bump jackson-databind from 2.13.3 to 2.13.4.1 (#948) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.3 to 2.13.4.1. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 09bb28375..a53bb7bf6 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.19.0 - 2.13.3 + 2.13.4.1 1.9.7 2.17.240 2.11.2 From 6fe74ea67100fc6719f73f347089cbeb36d7716b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:11:49 +0200 Subject: [PATCH 0202/1008] build(deps): bump assertj-core from 3.23.0 to 3.23.1 (#885) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.23.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.23.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a53bb7bf6..6660b92a4 100644 --- a/pom.xml +++ b/pom.xml @@ -262,7 +262,7 @@ org.assertj assertj-core - 3.23.0 + 3.23.1 test From fc28e540b757bf8b8aa394f77c089f80b2081d3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:20:09 +0200 Subject: [PATCH 0203/1008] build(deps): bump junit-jupiter.version from 5.9.0 to 5.9.1 (#950) Bumps `junit-jupiter.version` from 5.9.0 to 5.9.1. Updates `junit-jupiter-api` from 5.9.0 to 5.9.1 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) Updates `junit-jupiter-engine` from 5.9.0 to 5.9.1 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) Updates `junit-jupiter-params` from 5.9.0 to 5.9.1 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6660b92a4..032a3a57d 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ 3.4.1 3.2.1 3.0.1 - 5.9.0 + 5.9.1 1.0.6 0.5.1 From 9e1226dc7e536fffb31da3129381c648f87fa968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:20:24 +0200 Subject: [PATCH 0204/1008] build(deps): bump aws.xray.recorder.version from 2.11.2 to 2.12.0 (#949) Bumps `aws.xray.recorder.version` from 2.11.2 to 2.12.0. Updates `aws-xray-recorder-sdk-core` from 2.11.2 to 2.12.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.2...v2.12.0) Updates `aws-xray-recorder-sdk-aws-sdk-core` from 2.11.2 to 2.12.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.2...v2.12.0) Updates `aws-xray-recorder-sdk-aws-sdk-v2` from 2.11.2 to 2.12.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.2...v2.12.0) Updates `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.11.2 to 2.12.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.11.2...v2.12.0) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 032a3a57d..04ee4a0bd 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 2.13.4.1 1.9.7 2.17.240 - 2.11.2 + 2.12.0 2.1.2 UTF-8 1.2.1 From f8d3b11ab4c4f636142333a977e0a9b4838aecd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:40:57 +0200 Subject: [PATCH 0205/1008] build(deps): bump aws.sdk.version from 2.17.240 to 2.17.290 (#951) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps `aws.sdk.version` from 2.17.240 to 2.17.290. Updates `software.amazon.awssdk:bom` from 2.17.240 to 2.17.290 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.240...2.17.290) Updates `http-client-spi` from 2.17.240 to 2.17.290 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.240...2.17.290) Updates `url-connection-client` from 2.17.240 to 2.17.290 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jérôme Van Der Linden --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 04ee4a0bd..71381d861 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.1 1.9.7 - 2.17.240 + 2.17.290 2.12.0 2.1.2 UTF-8 From a4d479e2c244959eae2a42360abcda6f574ce71e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:29:32 +0200 Subject: [PATCH 0206/1008] build(deps): bump mockito-core from 4.7.0 to 4.8.1 (#958) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71381d861..cee7fe4f2 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.7.0 + 4.8.1 test From acf92d176390052909d4ab8d415773aeb05b2f46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:30:11 +0200 Subject: [PATCH 0207/1008] build(deps): bump mockito-inline from 4.6.1 to 4.8.1 (#957) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.6.1 to 4.8.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.8.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cee7fe4f2..880c91858 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.6.1 + 4.8.1 test From f309d68c678cf7c8f7a74d9bb025f82821ed9f90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:55:51 +0200 Subject: [PATCH 0208/1008] build(deps): bump spotbugs-maven-plugin from 4.6.0.0 to 4.7.2.1 (#955) Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.6.0.0 to 4.7.2.1. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.6.0.0...spotbugs-maven-plugin-4.7.2.1) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 880c91858..6733e37e3 100644 --- a/pom.xml +++ b/pom.xml @@ -457,7 +457,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.6.0.0 + 4.7.2.1 test From 8254e6080496ea72feb7d0f2b31d587212977430 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:56:38 +0200 Subject: [PATCH 0209/1008] build(deps): bump aws.sdk.version from 2.17.290 to 2.17.291 (#953) Bumps `aws.sdk.version` from 2.17.290 to 2.17.291. Updates `software.amazon.awssdk:bom` from 2.17.290 to 2.17.291 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.290...2.17.291) Updates `http-client-spi` from 2.17.290 to 2.17.291 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.290...2.17.291) Updates `url-connection-client` from 2.17.290 to 2.17.291 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6733e37e3..e39b85597 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.1 1.9.7 - 2.17.290 + 2.17.291 2.12.0 2.1.2 UTF-8 From 7cfc23597a01ea68fe9e53de5a5c3bcff58bd3f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:41:25 +0200 Subject: [PATCH 0210/1008] build(deps): bump aws.sdk.version from 2.17.291 to 2.18.4 (#963) Bumps `aws.sdk.version` from 2.17.291 to 2.18.4. Updates `software.amazon.awssdk:bom` from 2.17.291 to 2.18.4 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.291...2.18.4) Updates `http-client-spi` from 2.17.291 to 2.18.4 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.291...2.18.4) Updates `url-connection-client` from 2.17.291 to 2.18.4 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e39b85597..2a4b56972 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.1 1.9.7 - 2.17.291 + 2.18.4 2.12.0 2.1.2 UTF-8 From c556e38b57f68d8d54e88597c569bd944d5f450d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:41:53 +0200 Subject: [PATCH 0211/1008] build(deps): bump jackson-databind from 2.13.4.1 to 2.13.4.2 (#961) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.4.1 to 2.13.4.2. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a4b56972..9af93bf6f 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.19.0 - 2.13.4.1 + 2.13.4.2 1.9.7 2.18.4 2.12.0 From 8b4cc17ed51a594c96140eb4e0ff8412110875e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 11:27:49 +0100 Subject: [PATCH 0212/1008] build(deps): bump aws.sdk.version from 2.18.4 to 2.18.7 (#967) Bumps `aws.sdk.version` from 2.18.4 to 2.18.7. Updates `software.amazon.awssdk:bom` from 2.18.4 to 2.18.7 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.4...2.18.7) Updates `http-client-spi` from 2.18.4 to 2.18.7 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.4...2.18.7) Updates `url-connection-client` from 2.18.4 to 2.18.7 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9af93bf6f..bd6968161 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.2 1.9.7 - 2.18.4 + 2.18.7 2.12.0 2.1.2 UTF-8 From a0ef6d1283aa467be27793e487ae1d149b7696f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Wed, 2 Nov 2022 11:41:14 +0100 Subject: [PATCH 0213/1008] fix: envelope is not taken into account with built-in types (#960) * add precedence for the envelope over built-in types * cannot use same envelope for in and out * remove extra new line * handle event field names properly ex: SQS Records with big R * more explicit exception message --- powertools-validation/pom.xml | 10 +++---- .../validation/ValidationUtils.java | 12 +++++++- .../validation/internal/ValidationAspect.java | 12 ++++++-- .../SQSWithCustomEnvelopeHandler.java | 28 ++++++++++++++++++ .../handlers/SQSWithWrongEnvelopeHandler.java | 29 +++++++++++++++++++ .../internal/ValidationAspectTest.java | 18 ++++++++++++ .../src/test/resources/sqs_message.json | 22 ++++++++++++++ 7 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java create mode 100644 powertools-validation/src/test/resources/sqs_message.json diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 61beb34ba..c5a0a767c 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -75,6 +75,10 @@ json-schema-validator 1.0.73 + + com.amazonaws + aws-lambda-java-serialization + @@ -87,11 +91,7 @@ junit-jupiter-engine test - - com.amazonaws - aws-lambda-java-serialization - test - + org.apache.commons commons-lang3 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 83f34ebfd..12c51c632 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 @@ -13,6 +13,7 @@ */ package software.amazon.lambda.powertools.validation; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; @@ -21,9 +22,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; +import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; +import com.fasterxml.jackson.databind.node.NullNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.ValidationMessage; import io.burt.jmespath.Expression; @@ -65,9 +69,15 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope) } JsonNode subNode; try { - JsonNode jsonNode = ValidationConfig.get().getObjectMapper().valueToTree(obj); + PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(obj.getClass(), ClassLoader.getSystemClassLoader()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + pojoSerializer.toJson(obj, out); + JsonNode jsonNode = ValidationConfig.get().getObjectMapper().readTree(out.toString("UTF-8")); Expression expression = ValidationConfig.get().getJmesPath().compile(envelope); subNode = expression.search(jsonNode); + if (subNode == null || subNode instanceof NullNode) { + throw new ValidationException("Envelope not found in the object"); + } } catch (Exception e) { throw new ValidationException("Cannot find envelope <"+envelope+"> in the object <"+obj+">", e); } 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 0a1f00599..a9d43271b 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 @@ -19,6 +19,8 @@ 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.validation.Validation; import software.amazon.lambda.powertools.validation.ValidationConfig; @@ -36,6 +38,8 @@ */ @Aspect public class ValidationAspect { + private static final Logger LOG = LoggerFactory.getLogger(ValidationAspect.class); + @SuppressWarnings({"EmptyMethod"}) @Pointcut("@annotation(validation)") public void callAt(Validation validation) { @@ -59,7 +63,9 @@ && placedOnRequestHandler(pjp)) { JsonSchema inboundJsonSchema = getJsonSchema(validation.inboundSchema(), true); Object obj = pjp.getArgs()[0]; - if (obj instanceof APIGatewayProxyRequestEvent) { + if (validation.envelope() != null && !validation.envelope().isEmpty()) { + validate(obj, inboundJsonSchema, validation.envelope()); + } else if (obj instanceof APIGatewayProxyRequestEvent) { APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) obj; validate(event.getBody(), inboundJsonSchema); } else if (obj instanceof APIGatewayV2HTTPEvent) { @@ -105,7 +111,7 @@ && placedOnRequestHandler(pjp)) { KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else { - validate(obj, inboundJsonSchema, validation.envelope()); + LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", obj.getClass().getName()); } } } @@ -131,7 +137,7 @@ && placedOnRequestHandler(pjp)) { KinesisAnalyticsInputPreprocessingResponse response = (KinesisAnalyticsInputPreprocessingResponse) result; response.getRecords().forEach(record -> validate(decode(record.getData()), outboundJsonSchema)); } else { - validate(result, outboundJsonSchema, validation.envelope()); + LOG.warn("Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", result.getClass().getName()); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java new file mode 100644 index 000000000..9eb96c0e8 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.SQSEvent; +import software.amazon.lambda.powertools.validation.Validation; + +public class SQSWithCustomEnvelopeHandler implements RequestHandler { + + @Override + @Validation(inboundSchema = "classpath:/schema_v7.json", envelope = "Records[*].powertools_json(body).powertools_json(Message)") + public String handleRequest(SQSEvent input, Context context) { + return "OK"; + } +} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java new file mode 100644 index 000000000..c1859f29a --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.SQSEvent; +import software.amazon.lambda.powertools.validation.Validation; + +public class SQSWithWrongEnvelopeHandler implements RequestHandler { + + @Override + // real event contains Records with big R (https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html) + @Validation(inboundSchema = "classpath:/schema_v7.json", envelope = "records[*].powertools_json(body).powertools_json(Message)") + public String handleRequest(SQSEvent input, Context context) { + return "OK"; + } +} 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 c8741f1e3..2c66885d6 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 @@ -101,6 +101,24 @@ public void validate_SQS() { assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } + @Test + public void validate_SQS_CustomEnvelopeTakePrecedence() { + PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); + + SQSWithCustomEnvelopeHandler handler = new SQSWithCustomEnvelopeHandler(); + assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); + } + + @Test + public void validate_SQS_WrongEnvelope_shouldThrowValidationException() { + PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); + + SQSWithWrongEnvelopeHandler handler = new SQSWithWrongEnvelopeHandler(); + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> handler.handleRequest(event, context)); + } + @Test public void validate_Kinesis() { PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); diff --git a/powertools-validation/src/test/resources/sqs_message.json b/powertools-validation/src/test/resources/sqs_message.json new file mode 100644 index 000000000..068279b74 --- /dev/null +++ b/powertools-validation/src/test/resources/sqs_message.json @@ -0,0 +1,22 @@ +{ + "Records": [ + { + "messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db2", + "receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==", + "body": "{\n \"Message\": \"{\\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" + } + ] +} \ No newline at end of file From a9e6f90f919f712936523925384a8edd3b7e053f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 14:46:59 +0100 Subject: [PATCH 0214/1008] build(deps): bump aws.sdk.version from 2.18.7 to 2.18.8 (#968) Bumps `aws.sdk.version` from 2.18.7 to 2.18.8. Updates `software.amazon.awssdk:bom` from 2.18.7 to 2.18.8 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.7...2.18.8) Updates `http-client-spi` from 2.18.7 to 2.18.8 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.7...2.18.8) Updates `url-connection-client` from 2.18.7 to 2.18.8 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd6968161..a9b10af53 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.2 1.9.7 - 2.18.7 + 2.18.8 2.12.0 2.1.2 UTF-8 From 2b0a0abe42535d9ecd3f45c89ab3fce763bc07bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Nov 2022 14:02:08 +0100 Subject: [PATCH 0215/1008] build(deps): bump aws.sdk.version from 2.18.8 to 2.18.9 (#969) Bumps `aws.sdk.version` from 2.18.8 to 2.18.9. Updates `software.amazon.awssdk:bom` from 2.18.8 to 2.18.9 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.8...2.18.9) Updates `http-client-spi` from 2.18.8 to 2.18.9 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.8...2.18.9) Updates `url-connection-client` from 2.18.8 to 2.18.9 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a9b10af53..b9dfd769f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.2 1.9.7 - 2.18.8 + 2.18.9 2.12.0 2.1.2 UTF-8 From a07e5d9453284c6dea9244d4d619436f6aa590c9 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden Date: Fri, 4 Nov 2022 11:35:22 +0100 Subject: [PATCH 0216/1008] add link to commits conventions --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 94eb32733..db7e42bfc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,7 @@ * [ ] [Meet tenets criteria](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) * [ ] Update tests * [ ] Update docs -* [ ] PR title follows [conventional commit semantics]() +* [ ] PR title follows [conventional commit semantics](https://www.conventionalcommits.org/en/v1.0.0/) ## Breaking change checklist From e517de0acfcb85b9244bd20db836c0ab52e15477 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:12:51 +0100 Subject: [PATCH 0217/1008] build(deps): bump aws.sdk.version from 2.18.9 to 2.18.13 (#975) Bumps `aws.sdk.version` from 2.18.9 to 2.18.13. Updates `software.amazon.awssdk:bom` from 2.18.9 to 2.18.13 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.9...2.18.13) Updates `http-client-spi` from 2.18.9 to 2.18.13 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.9...2.18.13) Updates `url-connection-client` from 2.18.9 to 2.18.13 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b9dfd769f..588518fb9 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.13.4.2 1.9.7 - 2.18.9 + 2.18.13 2.12.0 2.1.2 UTF-8 From d2f2ee72ab69ffb1c96da8123e2d5d8225dc5b2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:13:21 +0100 Subject: [PATCH 0218/1008] build(deps): bump jackson-databind from 2.13.4.2 to 2.14.0 (#974) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.13.4.2 to 2.14.0. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 588518fb9..1cf8e6ef4 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.19.0 - 2.13.4.2 + 2.14.0 1.9.7 2.18.13 2.12.0 From d6405f41034807ce7507cc678de4889d682f02d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:13:48 +0100 Subject: [PATCH 0219/1008] build(deps): bump spotbugs-maven-plugin from 4.7.2.1 to 4.7.3.0 (#972) Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.7.2.1 to 4.7.3.0. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.7.2.1...spotbugs-maven-plugin-4.7.3.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1cf8e6ef4..0e7fe32eb 100644 --- a/pom.xml +++ b/pom.xml @@ -457,7 +457,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.7.2.1 + 4.7.3.0 test From 7a4a896c04133b5cc043c700ee10c13e0358deb2 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Tue, 15 Nov 2022 22:59:18 +0100 Subject: [PATCH 0220/1008] Fix for #577: Some code suggestion from CodeGuru (#984) * avoid repeated get() * %s to %d for integer * change put() to putIfAbsent() * Update powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java Co-authored-by: Michael Brewer * Fix logic for caching json schema. Co-authored-by: liuxinyu Co-authored-by: Pankaj Agrawal Co-authored-by: Michael Brewer --- .../parameters/cache/DataStore.java | 3 +- .../powertools/sqs/internal/BatchContext.java | 2 +- .../validation/ValidationUtils.java | 33 ++++++++++--------- 3 files changed, 21 insertions(+), 17 deletions(-) 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 351ba054d..9ad8df12c 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 @@ -46,7 +46,8 @@ public void remove(String Key){ } public Object get(String key) { - return store.containsKey(key)?store.get(key).value:null; + ValueNode node = store.get(key); + return node != null ? node.value : null; } public boolean hasExpired(String key, Instant now) { 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 index 93ad2abc4..1e4eff3bf 100644 --- 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 @@ -114,7 +114,7 @@ private void processFailedMessages(List successReturns, map(SQSMessage::getMessageId) .collect(toList()); - LOG.debug(format("[%s] records failed processing, but exceptions are suppressed. " + + LOG.debug(format("[%d] records failed processing, but exceptions are suppressed. " + "Failed messages %s", failedMessages.size(), messageIds)); } else { throw new SQSBatchProcessingException(exceptions, failedMessages, successReturns); 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 12c51c632..9b73806a5 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 @@ -239,12 +239,17 @@ public static JsonSchema getJsonSchema(String schema) { * @return the loaded json schema */ public static JsonSchema getJsonSchema(String schema, boolean validateSchema) { - JsonSchema jsonSchema = schemas.get(schema); + JsonSchema jsonSchema = schemas.computeIfAbsent(schema, ValidationUtils::createJsonSchema); - if (jsonSchema != null) { - return jsonSchema; + if (validateSchema) { + validateSchema(schema, jsonSchema); } + return jsonSchema; + } + + private static JsonSchema createJsonSchema(String schema) { + JsonSchema jsonSchema; if (schema.startsWith(CLASSPATH)) { String filePath = schema.substring(CLASSPATH.length()); try (InputStream schemaStream = ValidationAspect.class.getResourceAsStream(filePath)) { @@ -260,21 +265,19 @@ public static JsonSchema getJsonSchema(String schema, boolean validateSchema) { jsonSchema = ValidationConfig.get().getFactory().getSchema(schema); } - if (validateSchema) { - String version = ValidationConfig.get().getSchemaVersion().toString(); - try { - validate(jsonSchema.getSchemaNode(), - getJsonSchema("classpath:/schemas/meta_schema_" + version)); - } catch (ValidationException ve) { - throw new IllegalArgumentException("The schema " + schema + " is not valid, it does not respect the specification " + version, ve); - } - } - - schemas.put(schema, jsonSchema); - return jsonSchema; } + private static void validateSchema(String schema, JsonSchema jsonSchema) { + String version = ValidationConfig.get().getSchemaVersion().toString(); + try { + validate(jsonSchema.getSchemaNode(), + getJsonSchema("classpath:/schemas/meta_schema_" + version)); + } catch (ValidationException ve) { + throw new IllegalArgumentException("The schema " + schema + " is not valid, it does not respect the specification " + version, ve); + } + } + /** * */ From 32acf40e76f8a376ec2ab537d4cb2c123800cd2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 22:59:46 +0100 Subject: [PATCH 0221/1008] build(deps): bump mockito-inline from 4.8.1 to 4.9.0 (#986) Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.8.1 to 4.9.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.8.1...v4.9.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-inline dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0e7fe32eb..ac694a02f 100644 --- a/pom.xml +++ b/pom.xml @@ -250,7 +250,7 @@ org.mockito mockito-inline - 4.8.1 + 4.9.0 test From d686d2f011580e4b54d2a59319db6c1bf8285319 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 23:02:09 +0100 Subject: [PATCH 0222/1008] build(deps): bump aws.xray.recorder.version from 2.12.0 to 2.13.0 (#980) Bumps `aws.xray.recorder.version` from 2.12.0 to 2.13.0. Updates `aws-xray-recorder-sdk-core` from 2.12.0 to 2.13.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.12.0...v2.13.0) Updates `aws-xray-recorder-sdk-aws-sdk-core` from 2.12.0 to 2.13.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.12.0...v2.13.0) Updates `aws-xray-recorder-sdk-aws-sdk-v2` from 2.12.0 to 2.13.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.12.0...v2.13.0) Updates `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.12.0 to 2.13.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.12.0...v2.13.0) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ac694a02f..6fcc78e1e 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 2.14.0 1.9.7 2.18.13 - 2.12.0 + 2.13.0 2.1.2 UTF-8 1.2.1 From 823cc9a6fe58e755e19cfb484b2ce5958227168e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 23:02:51 +0100 Subject: [PATCH 0223/1008] build(deps): bump aws-lambda-java-core from 1.2.1 to 1.2.2 (#979) Bumps [aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs) from 1.2.1 to 1.2.2. - [Release notes](https://github.com/aws/aws-lambda-java-libs/releases) - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6fcc78e1e..e77597278 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 2.13.0 2.1.2 UTF-8 - 1.2.1 + 1.2.2 3.11.0 1.0.0 3.10.1 From 1a51897d498a1fb2ec1ca94e23823ddd3aef1611 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:40:16 +0100 Subject: [PATCH 0224/1008] build(deps): bump aws.sdk.version from 2.18.13 to 2.18.19 (#989) Bumps `aws.sdk.version` from 2.18.13 to 2.18.19. Updates `software.amazon.awssdk:bom` from 2.18.13 to 2.18.19 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.13...2.18.19) Updates `http-client-spi` from 2.18.13 to 2.18.19 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.13...2.18.19) Updates `url-connection-client` from 2.18.13 to 2.18.19 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e77597278..3466e633a 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.14.0 1.9.7 - 2.18.13 + 2.18.19 2.13.0 2.1.2 UTF-8 From 63be8bff314983b7d3233df736f228ea03855b25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:40:42 +0100 Subject: [PATCH 0225/1008] build(deps): bump mockito-core from 4.8.1 to 4.9.0 (#988) Bumps [mockito-core](https://github.com/mockito/mockito) from 4.8.1 to 4.9.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.8.1...v4.9.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3466e633a..e1a1ece35 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.mockito mockito-core - 4.8.1 + 4.9.0 test From 4135e55864a4dd5362f37a0b5664b116e87a1eff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:41:04 +0100 Subject: [PATCH 0226/1008] build(deps-dev): bump junit-pioneer from 1.7.1 to 1.9.0 (#985) Bumps [junit-pioneer](https://github.com/junit-pioneer/junit-pioneer) from 1.7.1 to 1.9.0. - [Release notes](https://github.com/junit-pioneer/junit-pioneer/releases) - [Commits](https://github.com/junit-pioneer/junit-pioneer/compare/v1.7.1...v1.9.0) --- updated-dependencies: - dependency-name: org.junit-pioneer:junit-pioneer dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 632619d45..25298709a 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -91,7 +91,7 @@ org.junit-pioneer junit-pioneer - 1.7.1 + 1.9.0 test From ace976c48330c1d496f8568b392814a64d5ad2fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:08:54 +0100 Subject: [PATCH 0227/1008] build(deps): bump aws.sdk.version from 2.18.19 to 2.18.22 (#993) Bumps `aws.sdk.version` from 2.18.19 to 2.18.22. Updates `software.amazon.awssdk:bom` from 2.18.19 to 2.18.22 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.19...2.18.22) Updates `http-client-spi` from 2.18.19 to 2.18.22 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.19...2.18.22) Updates `url-connection-client` from 2.18.19 to 2.18.22 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e1a1ece35..a4907b6a2 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.14.0 1.9.7 - 2.18.19 + 2.18.22 2.13.0 2.1.2 UTF-8 From ad339dc10424f957e858578c63737360c395cc69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:09:28 +0100 Subject: [PATCH 0228/1008] build(deps): bump jackson-databind from 2.14.0 to 2.14.1 (#992) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.14.0 to 2.14.1. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a4907b6a2..6f443e3a7 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.19.0 - 2.14.0 + 2.14.1 1.9.7 2.18.22 2.13.0 From bef38d75499c895e4d3f0e4e7968998cd4c7f2f4 Mon Sep 17 00:00:00 2001 From: Flavio Miamoto Date: Tue, 22 Nov 2022 14:09:54 -0300 Subject: [PATCH 0229/1008] docs: add missing grammar article (#976) Co-authored-by: Flavio Miamoto --- docs/utilities/sqs_large_message_handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md index 95dc34e35..65c4b7b4d 100644 --- a/docs/utilities/sqs_large_message_handling.md +++ b/docs/utilities/sqs_large_message_handling.md @@ -144,7 +144,7 @@ To disable deletion of payloads setting the following annotation parameter: ## 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 list of `SQSMessage` object enriched from S3 payload. +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. From 09f038aa5d2379ebaf75012cf737b02e083f177c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Thu, 24 Nov 2022 15:16:59 +0100 Subject: [PATCH 0230/1008] remove SqsLargeMessageAspect in DeclarePrecedence (#998) SqsLargeMessageAspect is not necessarily in the classpath the * before LambdaLoggingAspect includes SqsLargeMessageAspect --- .../lambda/powertools/logging/internal/LambdaLoggingAspect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 34f3bf312..f522a4711 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 @@ -58,7 +58,7 @@ import static software.amazon.lambda.powertools.logging.LoggingUtils.objectMapper; @Aspect -@DeclarePrecedence("*, SqsLargeMessageAspect, LambdaLoggingAspect") +@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(); From 4fb0384c4ae5c17f7af03c8eb2634559729470cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 15:36:08 +0100 Subject: [PATCH 0231/1008] build(deps): bump aws.sdk.version from 2.18.22 to 2.18.24 (#997) Bumps `aws.sdk.version` from 2.18.22 to 2.18.24. Updates `software.amazon.awssdk:bom` from 2.18.22 to 2.18.24 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.22...2.18.24) Updates `http-client-spi` from 2.18.22 to 2.18.24 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.22...2.18.24) Updates `url-connection-client` from 2.18.22 to 2.18.24 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6f443e3a7..471eab30e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.14.1 1.9.7 - 2.18.22 + 2.18.24 2.13.0 2.1.2 UTF-8 From d6115ac6c3b3963ef353b67980cc487105497f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= Date: Wed, 14 Dec 2022 10:07:13 +0100 Subject: [PATCH 0232/1008] feat(idempotency): handle lambda timeout scenarios for INPROGRESS records (#933) * fix idempotency when function timeouts --- .gitignore | 2 + docs/utilities/idempotency.md | 126 +++++++++++++++++- mkdocs.yml | 5 + powertools-idempotency/pom.xml | 1 + .../powertools/idempotency/Idempotency.java | 10 ++ .../idempotency/IdempotencyConfig.java | 12 +- ...IdempotencyInconsistentStateException.java | 2 +- .../internal/IdempotencyHandler.java | 29 +++- .../internal/IdempotentAspect.java | 19 ++- .../persistence/BasePersistenceStore.java | 26 +++- .../idempotency/persistence/DataRecord.java | 22 ++- .../persistence/DynamoDBPersistenceStore.java | 45 ++++++- .../handlers/IdempotencyInternalFunction.java | 10 ++ .../handlers/IdempotencyStringFunction.java | 22 +++ .../internal/IdempotencyAspectTest.java | 118 ++++++++++++++-- .../persistence/BasePersistenceStoreTest.java | 47 ++++++- .../DynamoDBPersistenceStoreTest.java | 99 +++++++++++++- spotbugs-exclude.xml | 8 ++ 18 files changed, 563 insertions(+), 40 deletions(-) create mode 100644 powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java diff --git a/.gitignore b/.gitignore index 12a60ce4d..04e85211d 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,8 @@ Desktop.ini ###################### /bin/ /deploy/ +/dist/ +/site/ ###################### # Logs diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 56b9acb78..17391218d 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -355,19 +355,141 @@ Imagine the function executes successfully, but the client never receives the re This sequence diagram shows an example flow of what happens in the payment scenario: -![Idempotent sequence](../media/idempotent_sequence.png) +
+```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Persistence Layer + alt initial request + Client->>Lambda: Invoke (event) + Lambda->>Persistence Layer: Get or set (id=event.search(payload)) + activate Persistence Layer + Note right of Persistence Layer: Locked to prevent concurrent
invocations with
the same payload. + Lambda-->>Lambda: Call handler (event) + Lambda->>Persistence Layer: Update record with result + deactivate Persistence Layer + Persistence Layer-->>Persistence Layer: Update record with result + Lambda-->>Client: Response sent to client + else retried request + Client->>Lambda: Invoke (event) + Lambda->>Persistence Layer: Get or set (id=event.search(payload)) + Persistence Layer-->>Lambda: Already exists in persistence layer. Return result + Lambda-->>Client: Response sent to client + end +``` +Idempotent sequence +
The client was successful in receiving the result after the retry. Since the Lambda handler was only executed once, our customer hasn't been charged twice. !!! note Bear in mind that the entire Lambda handler is treated as a single idempotent operation. If your Lambda handler can cause multiple side effects, consider splitting it into separate functions. +#### 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 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. + + 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> { + + public PaymentHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build()) + .configure(); + } + + @Override + public List 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); + } + + } + ``` + +#### Lambda timeout sequence diagram + +This sequence diagram shows an example flow of what happens if a Lambda function times out: + +
+```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Persistence Layer + alt initial request + Client->>Lambda: Invoke (event) + Lambda->>Persistence Layer: Get or set (id=event.search(payload)) + activate Persistence Layer + Note right of Persistence Layer: Locked to prevent concurrent
invocations with
the same payload. + Note over Lambda: Time out + Lambda--xLambda: Call handler (event) + Lambda-->>Client: Return error response + deactivate Persistence Layer + else concurrent request before timeout + Client->>Lambda: Invoke (event) + Lambda->>Persistence Layer: Get or set (id=event.search(payload)) + Persistence Layer-->>Lambda: Request already INPROGRESS + Lambda--xClient: Return IdempotencyAlreadyInProgressError + else retry after Lambda timeout + Client->>Lambda: Invoke (event) + Lambda->>Persistence Layer: Get or set (id=event.search(payload)) + activate Persistence Layer + Note right of Persistence Layer: Locked to prevent concurrent
invocations with
the same payload. + Lambda-->>Lambda: Call handler (event) + Lambda->>Persistence Layer: Update record with result + deactivate Persistence Layer + Persistence Layer-->>Persistence Layer: Update record with result + Lambda-->>Client: Response sent to client + end +``` +Idempotent sequence for Lambda timeouts +
+ ### 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**. 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. -![Idempotent sequence exception](../media/idempotent_sequence_exception.png) +
+```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Persistence Layer + Client->>Lambda: Invoke (event) + Lambda->>Persistence Layer: Get or set (id=event.search(payload)) + activate Persistence Layer + Note right of Persistence Layer: Locked during this time. Prevents multiple
Lambda invocations with the same
payload running concurrently. + Lambda--xLambda: Call handler (event).
Raises exception + Lambda->>Persistence Layer: Delete record (id=event.search(payload)) + deactivate Persistence Layer + Lambda-->>Client: Return error response +``` +Idempotent sequence exception +
If an Exception is raised _outside_ the scope of a decorated method and after your method has been called, the persistent record will not be affected. In this case, idempotency will be maintained for your decorated function. Example: diff --git a/mkdocs.yml b/mkdocs.yml index 574b8b30a..58e7fe757 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,6 +56,11 @@ markdown_extensions: - pymdownx.snippets: base_path: '.' check_paths: true + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format - meta - toc: permalink: true diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 25298709a..d84f38de1 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -122,6 +122,7 @@ com.amazonaws aws-lambda-java-tests + test com.amazonaws 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 index 1ff2ed47f..ce652791b 100644 --- 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 @@ -65,6 +65,16 @@ 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} * diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java index 4089d3ed8..28f20ffa9 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java @@ -13,6 +13,7 @@ */ package software.amazon.lambda.powertools.idempotency; +import com.amazonaws.services.lambda.runtime.Context; import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; import java.time.Duration; @@ -28,6 +29,7 @@ public class IdempotencyConfig { private final String payloadValidationJMESPath; private final boolean throwOnNoIdempotencyKey; private final String hashFunction; + private Context lambdaContext; private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, long expirationInSeconds, String hashFunction) { this.localCacheMaxItems = localCacheMaxItems; @@ -71,12 +73,20 @@ public String getHashFunction() { /** * Create a builder that can be used to configure and create a {@link IdempotencyConfig}. * - * @return a new instance of {@link IdempotencyConfig.Builder} + * @return a new instance of {@link Builder} */ public static Builder builder() { return new Builder(); } + public void setLambdaContext(Context lambdaContext) { + this.lambdaContext = lambdaContext; + } + + public Context getLambdaContext() { + return lambdaContext; + } + public static class Builder { private int localCacheMaxItems = 256; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java index c6fe38d23..40c90dcab 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java @@ -15,7 +15,7 @@ /** * IdempotencyInconsistentStateException can happen under rare but expected cases - * when persistent state changes in the small-time between put & get requests. + * when persistent state changes in the small-time between put & get requests. */ public class IdempotencyInconsistentStateException extends RuntimeException { private static final long serialVersionUID = -4293951999802300672L; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 1f3724919..c7e910bce 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -13,6 +13,7 @@ */ package software.amazon.lambda.powertools.idempotency.internal; +import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.databind.JsonNode; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; @@ -25,6 +26,7 @@ import software.amazon.lambda.powertools.utilities.JsonConfig; import java.time.Instant; +import java.util.OptionalInt; import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; @@ -40,10 +42,12 @@ public class IdempotencyHandler { private final ProceedingJoinPoint pjp; private final JsonNode data; private final BasePersistenceStore persistenceStore; + private final Context lambdaContext; - public IdempotencyHandler(ProceedingJoinPoint pjp, String functionName, JsonNode payload) { + public IdempotencyHandler(ProceedingJoinPoint pjp, String functionName, JsonNode payload, Context lambdaContext) { this.pjp = pjp; this.data = payload; + this.lambdaContext = lambdaContext; persistenceStore = Idempotency.getInstance().getPersistenceStore(); persistenceStore.configure(Idempotency.getInstance().getConfig(), functionName); } @@ -77,7 +81,7 @@ private Object processIdempotency() throws Throwable { try { // We call saveInProgress first as an optimization for the most common case where no idempotent record // already exists. If it succeeds, there's no need to call getRecord. - persistenceStore.saveInProgress(data, Instant.now()); + persistenceStore.saveInProgress(data, Instant.now(), getRemainingTimeInMillis()); } catch (IdempotencyItemAlreadyExistsException iaee) { DataRecord record = getIdempotencyRecord(); return handleForStatus(record); @@ -89,6 +93,21 @@ private Object processIdempotency() throws Throwable { return getFunctionResponse(); } + /** + * 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)} + * + * @return the remaining time in milliseconds or empty if the context was not provided/found + */ + private OptionalInt getRemainingTimeInMillis() { + if (lambdaContext != null) { + return OptionalInt.of(lambdaContext.getRemainingTimeInMillis()); + } else { + LOG.warn("Couldn't determine the remaining time left. Did you call registerLambdaContext on Idempotency?"); + } + return OptionalInt.empty(); + } + /** * Retrieve the idempotency record from the persistence layer. * @@ -121,12 +140,18 @@ private Object handleForStatus(DataRecord record) { } if (INPROGRESS.equals(record.getStatus())) { + if (record.getInProgressExpiryTimestamp().isPresent() + && record.getInProgressExpiryTimestamp().getAsLong() < Instant.now().toEpochMilli()) { + throw new IdempotencyInconsistentStateException("Item should have been expired in-progress because it already time-outed."); + } throw new IdempotencyAlreadyInProgressException("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(); return JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), returnType); } catch (Exception e) { throw new IdempotencyPersistenceLayerException("Unable to get function response as " + returnType.getSimpleName(), e); diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index 088e81249..8bc3c8c8a 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -19,7 +19,9 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; +import com.amazonaws.services.lambda.runtime.Context; import software.amazon.lambda.powertools.idempotency.Constants; +import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyKey; import software.amazon.lambda.powertools.idempotency.Idempotent; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; @@ -56,12 +58,20 @@ public Object around(ProceedingJoinPoint pjp, throw new IdempotencyConfigurationException("The annotated method doesn't return anything. Unable to perform idempotency on void return type"); } - JsonNode payload = getPayload(pjp, method); + boolean isHandler = (isHandlerMethod(pjp) && placedOnRequestHandler(pjp)); + JsonNode payload = getPayload(pjp, method, isHandler); if (payload == null) { throw new IdempotencyConfigurationException("Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); } - IdempotencyHandler idempotencyHandler = new IdempotencyHandler(pjp, method.getName(), payload); + Context lambdaContext; + if (isHandler) { + lambdaContext = (Context) pjp.getArgs()[1]; + } else { + lambdaContext = Idempotency.getInstance().getConfig().getLambdaContext(); + } + + IdempotencyHandler idempotencyHandler = new IdempotencyHandler(pjp, method.getName(), payload, lambdaContext); return idempotencyHandler.handle(); } @@ -71,11 +81,10 @@ public Object around(ProceedingJoinPoint pjp, * @param method the annotated method * @return the payload used for idempotency */ - private JsonNode getPayload(ProceedingJoinPoint pjp, Method method) { + private JsonNode getPayload(ProceedingJoinPoint pjp, Method method, boolean isHandler) { JsonNode payload = null; // handleRequest or method with one parameter: get the first one - if ((isHandlerMethod(pjp) && placedOnRequestHandler(pjp)) - || pjp.getArgs().length == 1) { + if (isHandler || pjp.getArgs().length == 1) { payload = JsonConfig.get().getObjectMapper().valueToTree(pjp.getArgs()[0]); } else { // Look for a parameter annotated with @IdempotencyKey diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index db0aaa688..79db29f05 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -36,9 +36,11 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; -import java.util.Spliterator; -import java.util.Spliterators; +import java.util.OptionalLong; +import java.util.OptionalInt; import java.util.stream.Stream; +import java.util.Spliterators; +import java.util.Spliterator; import java.util.stream.StreamSupport; /** @@ -108,7 +110,12 @@ public void configure(IdempotencyConfig config, String functionName) { public void saveSuccess(JsonNode data, Object result, Instant now) { ObjectWriter writer = JsonConfig.get().getObjectMapper().writer(); try { - String responseJson = writer.writeValueAsString(result); + String responseJson; + if (result instanceof String) { + responseJson = (String) result; + } else { + responseJson = writer.writeValueAsString(result); + } DataRecord record = new DataRecord( getHashedIdempotencyKey(data), DataRecord.Status.COMPLETED, @@ -131,19 +138,25 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { * @param data Payload * @param now */ - public void saveInProgress(JsonNode data, Instant now) throws IdempotencyItemAlreadyExistsException { + public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTimeInMs) throws IdempotencyItemAlreadyExistsException { String idempotencyKey = getHashedIdempotencyKey(data); if (retrieveFromCache(idempotencyKey, now) != null) { throw new IdempotencyItemAlreadyExistsException(); } + OptionalLong inProgressExpirationMsTimestamp = OptionalLong.empty(); + if (remainingTimeInMs.isPresent()) { + inProgressExpirationMsTimestamp = OptionalLong.of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); + } + DataRecord record = new DataRecord( idempotencyKey, DataRecord.Status.INPROGRESS, getExpiryEpochSecond(now), null, - getHashedPayload(data) + getHashedPayload(data), + inProgressExpirationMsTimestamp ); LOG.debug("saving in progress record for idempotency key: {}", record.getIdempotencyKey()); putRecord(record, now); @@ -212,7 +225,8 @@ private String getHashedIdempotencyKey(JsonNode data) { } String hash = generateHash(node); - return functionName + "#" + hash; + hash = functionName + "#" + hash; + return hash; } private boolean isMissingIdemPotencyKey(JsonNode data) { diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java index b4f58a73d..934ec3d09 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -13,8 +13,12 @@ */ 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.OptionalInt; +import java.util.OptionalLong; /** * Data Class for idempotency records. This is actually the item that will be stored in the persistence layer. @@ -25,6 +29,7 @@ public class DataRecord { private final long expiryTimestamp; private final String responseData; private final String payloadHash; + private final OptionalLong inProgressExpiryTimestamp; public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash) { this.idempotencyKey = idempotencyKey; @@ -32,6 +37,16 @@ public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, St this.expiryTimestamp = expiryTimestamp; this.responseData = responseData; this.payloadHash = payloadHash; + this.inProgressExpiryTimestamp = OptionalLong.empty(); + } + + public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash, OptionalLong inProgressExpiryTimestamp) { + this.idempotencyKey = idempotencyKey; + this.status = status.toString(); + this.expiryTimestamp = expiryTimestamp; + this.responseData = responseData; + this.payloadHash = payloadHash; + this.inProgressExpiryTimestamp = inProgressExpiryTimestamp; } public String getIdempotencyKey() { @@ -39,7 +54,7 @@ public String getIdempotencyKey() { } /** - * Check if data record is expired (based on expiration configured in the {@link software.amazon.lambda.powertools.idempotency.IdempotencyConfig}) + * Check if data record is expired (based on expiration configured in the {@link IdempotencyConfig}) * * @return Whether the record is currently expired or not */ @@ -60,6 +75,10 @@ public long getExpiryTimestamp() { return expiryTimestamp; } + public OptionalLong getInProgressExpiryTimestamp() { + return inProgressExpiryTimestamp; + } + public String getResponseData() { return responseData; } @@ -85,6 +104,7 @@ public int hashCode() { return Objects.hash(idempotencyKey, status, expiryTimestamp, responseData, payloadHash); } + /** * Status of the record: *
    diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 4985f845d..e48cb78e2 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -28,13 +28,14 @@ import java.time.Instant; import java.util.AbstractMap; -import java.util.Collections; 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.idempotency.Constants.AWS_REGION_ENV; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; /** * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.
    @@ -49,6 +50,8 @@ public class DynamoDBPersistenceStore extends BasePersistenceStore implements Pe private final String staticPkValue; private final String sortKeyAttr; private final String expiryAttr; + + private final String inProgressExpiryAttr; private final String statusAttr; private final String dataAttr; private final String validationAttr; @@ -62,6 +65,7 @@ private DynamoDBPersistenceStore(String tableName, String staticPkValue, String sortKeyAttr, String expiryAttr, + String inProgressExpiryAttr, String statusAttr, String dataAttr, String validationAttr, @@ -71,6 +75,7 @@ private DynamoDBPersistenceStore(String tableName, this.staticPkValue = staticPkValue; this.sortKeyAttr = sortKeyAttr; this.expiryAttr = expiryAttr; + this.inProgressExpiryAttr = inProgressExpiryAttr; this.statusAttr = statusAttr; this.dataAttr = dataAttr; this.validationAttr = validationAttr; @@ -115,6 +120,11 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre Map item = new HashMap<>(getKey(record.getIdempotencyKey())); item.put(this.expiryAttr, AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()); item.put(this.statusAttr, AttributeValue.builder().s(record.getStatus().toString()).build()); + + if (record.getInProgressExpiryTimestamp().isPresent()) { + item.put(this.inProgressExpiryAttr, AttributeValue.builder().n(String.valueOf(record.getInProgressExpiryTimestamp().getAsLong())).build()); + } + if (this.payloadValidationEnabled) { item.put(this.validationAttr, AttributeValue.builder().s(record.getPayloadHash()).build()); } @@ -124,16 +134,24 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre Map expressionAttributeNames = Stream.of( new AbstractMap.SimpleEntry<>("#id", this.keyAttr), - new AbstractMap.SimpleEntry<>("#expiry", this.expiryAttr)) + new AbstractMap.SimpleEntry<>("#expiry", this.expiryAttr), + new AbstractMap.SimpleEntry<>("#in_progress_expiry", this.inProgressExpiryAttr), + new AbstractMap.SimpleEntry<>("#status", this.statusAttr)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + Map expressionAttributeValues = Stream.of( + new AbstractMap.SimpleEntry<>(":now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()), + new AbstractMap.SimpleEntry<>(":inprogress", AttributeValue.builder().s(INPROGRESS.toString()).build()) + ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + dynamoDbClient.putItem( PutItemRequest.builder() .tableName(tableName) .item(item) - .conditionExpression("attribute_not_exists(#id) OR #expiry < :now") + .conditionExpression("attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now AND #status = :inprogress)") .expressionAttributeNames(expressionAttributeNames) - .expressionAttributeValues(Collections.singletonMap(":now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build())) + .expressionAttributeValues(expressionAttributeValues) .build() ); } catch (ConditionalCheckFailedException e) { @@ -212,12 +230,12 @@ private DataRecord itemToRecord(Map item) { // data and validation payload may be null AttributeValue data = item.get(this.dataAttr); AttributeValue validation = item.get(this.validationAttr); - return new DataRecord(item.get(sortKeyAttr != null ? sortKeyAttr: keyAttr).s(), DataRecord.Status.valueOf(item.get(this.statusAttr).s()), Long.parseLong(item.get(this.expiryAttr).n()), data != null ? data.s() : null, - validation != null ? validation.s() : null); + validation != null ? validation.s() : null, + item.get(this.inProgressExpiryAttr) != null ? OptionalLong.of(Long.parseLong(item.get(this.inProgressExpiryAttr).n())) : OptionalLong.empty()); } public static Builder builder() { @@ -238,6 +256,8 @@ public static class Builder { private String staticPkValue = String.format("idempotency#%s", funcEnv != null ? funcEnv : ""); private String sortKeyAttr; private String expiryAttr = "expiration"; + + private String inProgressExpiryAttr = "in_progress_expiration"; private String statusAttr = "status"; private String dataAttr = "data"; private String validationAttr = "validation"; @@ -256,7 +276,7 @@ public DynamoDBPersistenceStore build() { if (StringUtils.isEmpty(tableName)) { throw new IllegalArgumentException("Table name is not specified"); } - return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); + return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, inProgressExpiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); } /** @@ -315,6 +335,17 @@ public Builder withExpiryAttr(String expiryAttr) { return this; } + /** + * DynamoDB attribute name for in progress expiry timestamp (optional), by default "in_progress_expiration" + * + * @param inProgressExpiryAttr name of the attribute in the table + * @return the builder instance (to chain operations) + */ + public Builder withInProgressExpiryAttr(String inProgressExpiryAttr) { + this.inProgressExpiryAttr = inProgressExpiryAttr; + return this; + } + /** * DynamoDB attribute name for status (optional), by default "status" * diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java index 549d9e7ed..f3c1bdbc9 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java @@ -15,6 +15,7 @@ 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.IdempotencyKey; import software.amazon.lambda.powertools.idempotency.Idempotent; import software.amazon.lambda.powertools.idempotency.model.Basket; @@ -25,10 +26,19 @@ */ public class IdempotencyInternalFunction implements RequestHandler { + private final boolean registerContext; private boolean called = false; + public IdempotencyInternalFunction(boolean registerContext) { + this.registerContext = registerContext; + } + @Override public Basket handleRequest(Product input, Context context) { + if (registerContext) { + Idempotency.registerLambdaContext(context); + } + return createBasket("fake", input); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java new file mode 100644 index 000000000..e6d719598 --- /dev/null +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java @@ -0,0 +1,22 @@ +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.Idempotent; +import software.amazon.lambda.powertools.idempotency.model.Product; + +public class IdempotencyStringFunction implements RequestHandler { + + private boolean handlerCalled = false; + + public boolean handlerCalled() { + return handlerCalled; + } + + @Override + @Idempotent + public String handleRequest(Product input, Context context) { + handlerCalled = true; + return input.getName(); + } +} diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java index fc91c6c61..56687e338 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -27,6 +27,7 @@ import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyAlreadyInProgressException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyInconsistentStateException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.handlers.*; import software.amazon.lambda.powertools.idempotency.model.Basket; @@ -36,6 +37,8 @@ import software.amazon.lambda.powertools.utilities.JsonConfig; import java.time.Instant; +import java.util.OptionalInt; +import java.util.OptionalLong; import static java.time.temporal.ChronoUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; @@ -67,17 +70,22 @@ public void firstCall_shouldPutInStore() { 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(function.handlerCalled()).isTrue(); ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); - verify(store).saveInProgress(nodeCaptor.capture(), any()); + ArgumentCaptor 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 resultCaptor = ArgumentCaptor.forClass(Basket.class); verify(store).saveSuccess(any(), resultCaptor.capture(), any()); assertThat(resultCaptor.getValue()).isEqualTo(basket); @@ -93,7 +101,7 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce .build() ).configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any()); + doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); @@ -114,6 +122,36 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce assertThat(function.handlerCalled()).isFalse(); } + @Test + public void secondCall_notExpired_shouldGetStringFromStore() { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build() + ).configure(); + + doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + + Product p = new Product(42, "fake product", 12); + DataRecord record = new DataRecord( + "42", + DataRecord.Status.COMPLETED, + Instant.now().plus(356, SECONDS).getEpochSecond(), + p.getName(), + null); + doReturn(record).when(store).getRecord(any(), any()); + + // WHEN + IdempotencyStringFunction function = new IdempotencyStringFunction(); + String name = function.handleRequest(p, context); + + // THEN + assertThat(name).isEqualTo(p.getName()); + assertThat(function.handlerCalled()).isFalse(); + } + @Test public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() throws JsonProcessingException { // GIVEN @@ -124,16 +162,18 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti .build() ).configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any()); + doThrow(IdempotencyItemAlreadyExistsException.class).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( "42", DataRecord.Status.INPROGRESS, Instant.now().plus(356, SECONDS).getEpochSecond(), JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), - null); + null, + timestampInFuture); doReturn(record).when(store).getRecord(any(), any()); // THEN @@ -141,6 +181,35 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyAlreadyInProgressException.class); } + @Test + public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() throws JsonProcessingException { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build() + ).configure(); + + doThrow(IdempotencyItemAlreadyExistsException.class).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( + "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()); + + // THEN + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyInconsistentStateException.class); + } + @Test public void functionThrowException_shouldDeleteRecord_andThrowFunctionException() { // GIVEN @@ -190,7 +259,37 @@ public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { .configure(); // WHEN - IdempotencyInternalFunction function = new IdempotencyInternalFunction(); + 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); + + // THEN + assertThat(basket.getProducts()).hasSize(2); + assertThat(function.subMethodCalled()).isTrue(); + + ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + ArgumentCaptor expiryCaptor = ArgumentCaptor.forClass(OptionalInt.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), expiryCaptor.capture()); + assertThat(nodeCaptor.getValue().asText()).isEqualTo("fake"); + assertThat(expiryCaptor.getValue().orElse(-1)).isEqualTo(30000); + + ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), new Product(0, "fake", 0)); + } + + @Test + public void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + // WHEN + boolean registerContext = false; + IdempotencyInternalFunction function = new IdempotencyInternalFunction(registerContext); Product p = new Product(42, "fake product", 12); Basket basket = function.handleRequest(p, context); @@ -199,8 +298,10 @@ public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { assertThat(function.subMethodCalled()).isTrue(); ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); - verify(store).saveInProgress(nodeCaptor.capture(), any()); + ArgumentCaptor expiryCaptor = ArgumentCaptor.forClass(OptionalInt.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), expiryCaptor.capture()); assertThat(nodeCaptor.getValue().asText()).isEqualTo("fake"); + assertThat(expiryCaptor.getValue()).isEmpty(); ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); verify(store).saveSuccess(any(), resultCaptor.capture(), any()); @@ -214,7 +315,7 @@ public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromS .withPersistenceStore(store) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any()); + doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); @@ -227,7 +328,7 @@ public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromS doReturn(record).when(store).getRecord(any(), any()); // WHEN - IdempotencyInternalFunction function = new IdempotencyInternalFunction(); + IdempotencyInternalFunction function = new IdempotencyInternalFunction(false); Basket basket = function.handleRequest(p, context); // THEN @@ -285,4 +386,5 @@ public void idempotencyOnSubMethodVoid_shouldThrowException() { // THEN assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); } + } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index dac9a9288..e77a995a4 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -32,6 +32,7 @@ import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.OptionalInt; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -83,12 +84,30 @@ public void saveInProgress_defaultConfig() { persistenceStore.configure(IdempotencyConfig.builder().build(), null); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); 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#47261bd5b456f400f8d191cfb3a7482f"); assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(dr.getInProgressExpiryTimestamp()).isEmpty(); + assertThat(status).isEqualTo(1); + } + + @Test + public void saveInProgress_withRemainingTime() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore.configure(IdempotencyConfig.builder().build(), null); + + int lambdaTimeoutMs = 30000; + Instant now = Instant.now(); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.of(lambdaTimeoutMs)); + 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(dr.getPayloadHash()).isEqualTo(""); + assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo(now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); assertThat(status).isEqualTo(1); } @@ -100,7 +119,7 @@ public void saveInProgress_jmespath() { .build(), "myfunc"); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); @@ -117,7 +136,7 @@ public void saveInProgress_jmespath_NotFound_shouldThrowException() { .withThrowOnNoIdempotencyKey(true) // should throw .build(), ""); Instant now = Instant.now(); - assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now)) + assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) .isInstanceOf(IdempotencyKeyException.class) .hasMessageContaining("No data found to create a hashed idempotency key"); assertThat(status).isEqualTo(-1); @@ -130,7 +149,7 @@ public void saveInProgress_jmespath_NotFound_shouldNotThrowException() { .withEventKeyJMESPath("unavailable") .build(), ""); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(status).isEqualTo(1); } @@ -151,7 +170,7 @@ public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), null, null) ); - assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now)) + assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) .isInstanceOf(IdempotencyItemAlreadyExistsException.class); assertThat(status).isEqualTo(-1); } @@ -173,7 +192,7 @@ public void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), null, null) ); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(cache).isEmpty(); assertThat(status).isEqualTo(1); @@ -362,4 +381,20 @@ public void generateHashDouble_shouldGenerateMd5ofDouble() { String generatedHash = persistenceStore.generateHash(new DoubleNode(256.42)); assertThat(generatedHash).isEqualTo(expectedHash); } + + @Test + public void generateHashString_withSha256Algorithm_shouldGenerateSha256ofString() { + persistenceStore.configure(IdempotencyConfig.builder().withHashFunction("SHA-256").build(), null); + 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() { + persistenceStore.configure(IdempotencyConfig.builder().withHashFunction("HASH").build(), null); + String expectedHash = "70c24d88041893f7fbab4105b76fd9e1"; // MD5(Lambda rocks) + String generatedHash = persistenceStore.generateHash(new TextNode("Lambda rocks")); + assertThat(generatedHash).isEqualTo(expectedHash); + } } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java index 0da516324..86e35cd33 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java @@ -57,6 +57,68 @@ public void putRecord_shouldCreateRecordInDynamoDB() throws IdempotencyItemAlrea assertThat(item.get("expiration").n()).isEqualTo(String.valueOf(expiry)); } + @Test + public void putRecord_shouldCreateRecordInDynamoDB_IfPreviousExpired() { + key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); + + // GIVEN: Insert a fake item with same id and expired + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.minus(30, ChronoUnit.SECONDS).getEpochSecond(); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.COMPLETED.toString()).build()); + item.put("data", AttributeValue.builder().s("Fake Data").build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + + // WHEN: call putRecord + long expiry2 = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); + dynamoDBPersistenceStore.putRecord( + new DataRecord("key", + DataRecord.Status.INPROGRESS, + expiry2, + null, + null + ), now); + + // THEN: an item is inserted + Map 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() { + 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) + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); + long progressExpiry = now.minus(30, ChronoUnit.SECONDS).getEpochSecond(); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); + item.put("data", AttributeValue.builder().s("Fake Data").build()); + item.put("in_progress_expiration", AttributeValue.builder().n(String.valueOf(progressExpiry)).build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + + // WHEN: call putRecord + long expiry2 = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); + dynamoDBPersistenceStore.putRecord( + new DataRecord("key", + DataRecord.Status.INPROGRESS, + expiry2, + null, + null + ), now); + + // THEN: an item is inserted + Map 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() { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); @@ -65,7 +127,7 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA Map item = new HashMap<>(key); Instant now = Instant.now(); long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); - item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); // not expired item.put("status", AttributeValue.builder().s(DataRecord.Status.COMPLETED.toString()).build()); item.put("data", AttributeValue.builder().s("Fake Data").build()); client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); @@ -89,6 +151,41 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA assertThat(itemInDb.get("data").s()).isEqualTo("Fake Data"); } + @Test + public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordAlreadyExistAndProgressNotExpiredAfterLambdaTimedOut() { + key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); + + // GIVEN: Insert a fake item with same id + Map item = new HashMap<>(key); + Instant now = Instant.now(); + long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); // not expired + long progressExpiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); // not expired + item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); + item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); + item.put("data", AttributeValue.builder().s("Fake Data").build()); + item.put("in_progress_expiration", AttributeValue.builder().n(String.valueOf(progressExpiry)).build()); + client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); + + // 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); + + // THEN: item was not updated, retrieve the initial one + Map 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"); + } + + // // ================================================================= diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 0c8d1d8f8..fe4194bfd 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -32,6 +32,10 @@ + + + + @@ -69,6 +73,10 @@ + + + + From e3100cb80790e23e616ac815120720883c6e34e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:08:32 +0000 Subject: [PATCH 0233/1008] build(deps-dev): bump junit-pioneer from 1.9.0 to 1.9.1 (#1003) Bumps [junit-pioneer](https://github.com/junit-pioneer/junit-pioneer) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/junit-pioneer/junit-pioneer/releases) - [Commits](https://github.com/junit-pioneer/junit-pioneer/compare/v1.9.0...v1.9.1) --- updated-dependencies: - dependency-name: org.junit-pioneer:junit-pioneer dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index d84f38de1..7a41d2c9b 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -91,7 +91,7 @@ org.junit-pioneer junit-pioneer - 1.9.0 + 1.9.1 test From 521a8b7f57f5c302b059067f0537da988379bdad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:10:10 +0000 Subject: [PATCH 0234/1008] build(deps): bump aws.sdk.version from 2.18.24 to 2.18.37 (#1017) Bumps `aws.sdk.version` from 2.18.24 to 2.18.37. Updates `software.amazon.awssdk:bom` from 2.18.24 to 2.18.37 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.24...2.18.37) Updates `http-client-spi` from 2.18.24 to 2.18.37 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.18.24...2.18.37) Updates `url-connection-client` from 2.18.24 to 2.18.37 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 471eab30e..eb024d232 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.14.1 1.9.7 - 2.18.24 + 2.18.37 2.13.0 2.1.2 UTF-8 From 27aaab1763cee17699c0f8ffd361f0ca7f444bb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:10:55 +0000 Subject: [PATCH 0235/1008] build(deps): bump json-schema-validator from 1.0.73 to 1.0.75 (#1016) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.73 to 1.0.75. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.73...1.0.75) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index c5a0a767c..f7ad36a80 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.73 + 1.0.75 com.amazonaws From 99669c205af6a2ab9564d5153800b2f0a65cb14e Mon Sep 17 00:00:00 2001 From: Neoman Nouiouat Date: Wed, 14 Dec 2022 01:13:33 -0800 Subject: [PATCH 0236/1008] Log exceptions processing messages as occur (#1011) This should help make debugging issues easier when investigating issues processing messages and provide more precise time of exception in logs. --- .../java/software/amazon/lambda/powertools/sqs/SqsUtils.java | 1 + 1 file changed, 1 insertion(+) 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 index e8bf9a7ae..9ae81b77d 100644 --- 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 @@ -497,6 +497,7 @@ public static List batchProcessor(final SQSEvent event, batchContext.addSuccess(message); } catch (Exception e) { batchContext.addFailure(message, e); + LOG.error("Encountered issue processing message: {}", message.getMessageId(), e); } } From 2536b7fbdb1e47caa284240b0d325c309045f2ac Mon Sep 17 00:00:00 2001 From: Mark Sailes <45629314+msailes@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:46:38 +0000 Subject: [PATCH 0237/1008] chore: update the project version to 1.13.0 (#1018) --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 33 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 005680a4d..1e082ddb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,23 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.13.0] - 2022-12-14 + +### Added + +* Feature: Idempotency - Handle Lambda timeout scenarios for INPROGRESS records (#933) by @jeromevdl + +### Bug Fixes + +* 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 + +* Docs: Add missing grammar article (#976) by @fsmiamoto + ## [1.12.3] - 2022-07-12 ### Maintenance diff --git a/README.md b/README.md index 68827a999..d47a7306d 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.12.3 + 1.13.0 software.amazon.lambda powertools-logging - 1.12.3 + 1.13.0 software.amazon.lambda powertools-metrics - 1.12.3 + 1.13.0 ... diff --git a/mkdocs.yml b/mkdocs.yml index 58e7fe757..aef84c1e6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -81,7 +81,7 @@ extra_javascript: extra: powertools: - version: 1.12.3 + version: 1.13.0 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index eb024d232..6eec4c461 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.12.3 + 1.13.0 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 2547e717e..823f04a99 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index f54c24e12..06a6768d5 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7a41d2c9b..51230dc42 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.12.3 + 1.13.0 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 59a98b4a9..23ae2c345 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 1f83a6c19..d382588da 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index fb05bf738..313048a1e 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 69176ea3a..4e52c3928 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index f3ebd5e58..79a7b40d2 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 95f9a8b22..8f6e8ba69 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 59221ac4d..5f810ffb0 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index f7ad36a80..5e6a566c1 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.12.3 + 1.13.0 AWS Lambda Powertools for Java validation library From 5c465522db871dadb76842a46321e0dc3aa58337 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Sat, 21 Jan 2023 09:18:27 +0100 Subject: [PATCH 0238/1008] Add information about other supported langauges to README and docs. (#1033) --- README.md | 5 ++++- docs/index.md | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d47a7306d..ee21efd0b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,10 @@ ![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) -A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. ([AWS Lambda Powertools Python](https://github.com/awslabs/aws-lambda-powertools-python) is also available). +A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. + + +> Also available in [Python](https://github.com/awslabs/aws-lambda-powertools-python), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet). **[📜Documentation](https://awslabs.github.io/aws-lambda-powertools-java/)** | **[Feature request](https://github.com/awslabs/aws-lambda-powertools-java/issues/new?assignees=&labels=feature-request%2C+triage&template=feature_request.md&title=)** | **[🐛Bug Report](https://github.com/awslabs/aws-lambda-powertools-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/)** diff --git a/docs/index.md b/docs/index.md index ae1899ef6..d82a01e16 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,6 +7,10 @@ description: AWS Lambda Powertools for Java Powertools is a suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. +???+ tip + Powertools is also available for [Python](https://awslabs.github.io/aws-lambda-powertools-python/){target="_blank"}, [TypeScript](https://awslabs.github.io/aws-lambda-powertools-typescript/){target="_blank"}, and [.NET](https://awslabs.github.io/aws-lambda-powertools-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. From 35b8a43c02d914ca4b28298fb5aa7d5b62065e60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 08:49:48 +0100 Subject: [PATCH 0239/1008] build(deps): bump aws.sdk.version from 2.18.37 to 2.19.0 (#1024) Bumps `aws.sdk.version` from 2.18.37 to 2.19.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6eec4c461..23a79e7d6 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.14.1 1.9.7 - 2.18.37 + 2.19.0 2.13.0 2.1.2 UTF-8 From eda4031f0678ada9217fe51426b8dc96e362a5fc Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Thu, 9 Feb 2023 08:50:50 +0100 Subject: [PATCH 0240/1008] Bump Mockito to latest compatible version. (#1032) Configure dependabot to avoid bumping Mockito to v. 5. --- .github/dependabot.yml | 4 ++++ pom.xml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index caf24d1f9..743eaa616 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,3 +7,7 @@ updates: labels: - "maven" - "dependencies" + ignore: + # Ignore Mockito 5.X.X as it does not support Java 8 + - dependency-name: mockito-* + update-types: ["version-update:semver-major"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 23a79e7d6..ff73f79f5 100644 --- a/pom.xml +++ b/pom.xml @@ -244,13 +244,13 @@ org.mockito mockito-core - 4.9.0 + 4.11.0 test org.mockito mockito-inline - 4.9.0 + 4.11.0 test From ebb1b23e117f525613344025975c4e9b45e6088e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 08:53:31 +0100 Subject: [PATCH 0241/1008] build(deps): bump json-schema-validator from 1.0.75 to 1.0.76 (#1025) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.75 to 1.0.76. --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 5e6a566c1..e46d01294 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.75 + 1.0.76 com.amazonaws From 55979e336dd2e7918369887b3c53b54dca973be1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:01:15 +0100 Subject: [PATCH 0242/1008] build(deps): bump junit-jupiter.version from 5.9.1 to 5.9.2 (#1042) Bumps `junit-jupiter.version` from 5.9.1 to 5.9.2. Updates `junit-jupiter-api` from 5.9.1 to 5.9.2 Updates `junit-jupiter-engine` from 5.9.1 to 5.9.2 Updates `junit-jupiter-params` from 5.9.1 to 5.9.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff73f79f5..208fe3d66 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ 3.4.1 3.2.1 3.0.1 - 5.9.1 + 5.9.2 1.0.6 0.5.1 From 6062f5358679909cd898785f6ce26f68dd381bda Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Fri, 10 Feb 2023 11:25:40 +0100 Subject: [PATCH 0243/1008] Update logic responsible for recording pages views to use correct runtime. (#1047) --- docs/javascript/extra.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/javascript/extra.js b/docs/javascript/extra.js index 0ade322a6..6e9193366 100644 --- a/docs/javascript/extra.js +++ b/docs/javascript/extra.js @@ -9,9 +9,11 @@ const awsconfig = { "aws_kinesis_firehose_stream_name": "ClickStreamKinesisFirehose-OGX7PQdrynUo", }; -const RUNTIME = "python" +const RUNTIME = "java" +const BASE_ORIGIN = "awslabs.github.io" -const attachListeners = () => { +function enableSearchOnBlurElement() { + if (document.location.hostname != BASE_ORIGIN) return // prevent unnecessary data /* Register handler to log search on blur */ document.addEventListener("DOMContentLoaded", function () { recordPageView({ @@ -23,7 +25,6 @@ const attachListeners = () => { // If Search result is ever actionable // we should populate `value` if (this.value) { - let path = document.location.pathname; console.info(`Search value: ${this.value}`) recordPageView({ searchPattern: this.value @@ -41,6 +42,10 @@ const attachListeners = () => { }; } +const attachListeners = () => { + enableSearchOnBlurElement() +} + const init = () => { Analytics.addPluggable(new KinesisFirehoseProvider()) Amplify.configure(awsconfig); @@ -68,4 +73,4 @@ const recordPageView = ({prevLocation, searchPattern}) => { }, 'AWSKinesisFirehose') } -init() +init() \ No newline at end of file From 1f4b89c72bd762532814bac271d7da715f3660da Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Wed, 15 Feb 2023 11:03:05 +0000 Subject: [PATCH 0244/1008] docs(home): update powertools definition --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ee21efd0b..378b580ac 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ ![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) -A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. - +Powertools is a developer toolkit to implement Serverless best practices and increase developer velocity. > Also available in [Python](https://github.com/awslabs/aws-lambda-powertools-python), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet). From 7dfe9c37370828bd7c187edfc8793354dc6a6d04 Mon Sep 17 00:00:00 2001 From: Ahmed Kamel Date: Thu, 16 Feb 2023 10:36:52 +0000 Subject: [PATCH 0245/1008] feat(metrics): introduce MetricsUtils.withMetricsLogger utility (#1000) feat(metrics): introduce MetricsUtils.withMetricsLogger utility (#1000) --- docs/core/metrics.md | 26 ++++++++++++- .../powertools/metrics/MetricsUtils.java | 29 ++++++++++----- .../powertools/metrics/MetricsLoggerTest.java | 37 +++++++++++++++++-- 3 files changed, 78 insertions(+), 14 deletions(-) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 4d250df48..f84508669 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -233,4 +233,28 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use `withSingle }); } } - ``` \ No newline at end of file + ``` + +## Creating metrics with different configurations + +Use `withMetricsLogger` if you have one or more metrics that should have different configurations e.g. dimensions or namespace. + +=== "App.java" + + ```java hl_lines="7 8 9 10 11 12 13" + import static software.amazon.lambda.powertools.metrics.MetricsUtils.withMetricsLogger; + + public class App implements RequestHandler { + + @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); + }); + } + } + ``` 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 index 443ef3976..d58dd52d6 100644 --- 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 @@ -81,16 +81,10 @@ public static void withSingleMetric(final String name, final double value, final Unit unit, final Consumer logger) { - MetricsLogger metricsLogger = logger(); - - try { - metricsLogger.setNamespace(defaultNameSpace()); + withMetricLogger(metricsLogger -> { metricsLogger.putMetric(name, value, unit); - captureRequestAndTraceId(metricsLogger); logger.accept(metricsLogger); - } finally { - metricsLogger.flush(); - } + }); } /** @@ -109,11 +103,26 @@ public static void withSingleMetric(final String name, final Unit unit, final String namespace, final Consumer logger) { + withMetricLogger(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 withMetricLogger(final Consumer logger) { MetricsLogger metricsLogger = logger(); try { - metricsLogger.setNamespace(namespace); - metricsLogger.putMetric(name, value, unit); + metricsLogger.setNamespace(defaultNameSpace()); captureRequestAndTraceId(metricsLogger); logger.accept(metricsLogger); } finally { 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 index 449f12815..20f56c3e2 100644 --- 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 @@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -16,9 +15,9 @@ import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; -import static java.util.Collections.*; -import static org.assertj.core.api.Assertions.*; +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; @@ -123,6 +122,38 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { } } + @Test + void metricsLoggerCaptureUtilityWithDefaultNameSpace() { + try (MockedStatic mocked = mockStatic(SystemWrapper.class); + MockedStatic 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.withMetricLogger(metricsLogger -> { + metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); + metricsLogger.putMetric("Metric1", 1, Unit.COUNT); + }); + + assertThat(out.toString()) + .satisfies(s -> { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); + } + } + @Test void shouldThrowExceptionWhenDefaultDimensionIsNull() { assertThatNullPointerException() From d73980d0f7a937b539497fc9d7e6811ecf103520 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:39:28 +0100 Subject: [PATCH 0246/1008] build(deps): bump jackson-databind from 2.14.1 to 2.14.2 (#1045) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.14.1 to 2.14.2. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 208fe3d66..f8c0d8553 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.8 1.8 2.19.0 - 2.14.1 + 2.14.2 1.9.7 2.19.0 2.13.0 From 2ce81bd06843d515d2775d8b9322410b3fc1fece Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:40:03 +0100 Subject: [PATCH 0247/1008] build(deps): bump aws.sdk.version from 2.19.0 to 2.20.0 (#1044) Bumps `aws.sdk.version` from 2.19.0 to 2.20.0. Updates `software.amazon.awssdk:bom` from 2.19.0 to 2.20.0 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/docs/LaunchChangelog.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.19.0...2.20.0) Updates `http-client-spi` from 2.19.0 to 2.20.0 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/docs/LaunchChangelog.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.19.0...2.20.0) Updates `url-connection-client` from 2.19.0 to 2.20.0 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f8c0d8553..76f72b2d9 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.19.0 2.14.2 1.9.7 - 2.19.0 + 2.20.0 2.13.0 2.1.2 UTF-8 From 3037898173b9e4fdfab8fe06b544b09fb042e626 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub Date: Fri, 17 Feb 2023 09:41:45 +0100 Subject: [PATCH 0248/1008] chore: update the project version to 1.14.0 (#1052) --- CHANGELOG.md | 26 +++++++++++++++++++++----- README.md | 6 +++--- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 15 files changed, 37 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e082ddb9..57a1f5d6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.14.0] - 2023-02-17 + +### Added + +* Feature: Introduce `MetricsUtils.withMetricsLogger()` utility method (#1000) by @humanzz + +#### Maintenance + +* 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 + +* Docs: Update PowerTools definition by @heitorlessa +* Docs: Add information about other supported langauges to README and docs (#1033) by @kozub + ## [1.13.0] - 2022-12-14 ### Added @@ -27,7 +43,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [1.12.3] - 2022-07-12 -### Maintenance +#### Maintenance * Fixes to resolve vulnerable transitive dependencies ([919](https://github.com/awslabs/aws-lambda-powertools-java/issues/919)) @@ -47,7 +63,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo * **Idempotency**: disable dynamodb client creation in persistent store when disabling idempotency ([#796](https://github.com/awslabs/aws-lambda-powertools-java/pull/796)) -## Maintenance +### Maintenance * **deps**: Bump third party dependencies to the latest versions. @@ -121,7 +137,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo * **Logging**: `@Logging` annotation now works with `@Tracing` annotation on `RequestStreamHandler` when used in `logEvent` mode. [#567](https://github.com/awslabs/aws-lambda-powertools-java/pull/567) -## Maintenance +### Maintenance * **deps**: Bump third party dependencies to the latest versions. @@ -147,7 +163,7 @@ Users no longer need to depend on [com.nickwongdev](https://mvnrepository.com/ar * **Logging**: Support for extracting Correlation id using `@Logging` annotation via `correlationIdPath` attribute and `setCorrelationId()` method in `LoggingUtils`([#448](https://github.com/awslabs/aws-lambda-powertools-java/pull/448)). * **Logging**: New `clearState` attribute on `@Logging` annotation to clear previously added custom keys upon invocation([#453](https://github.com/awslabs/aws-lambda-powertools-java/pull/453)). -## Maintenance +### Maintenance * **deps**: Bump third party dependencies to the latest versions. @@ -158,7 +174,7 @@ Users no longer need to depend on [com.nickwongdev](https://mvnrepository.com/ar * **Tracing**: Support for Boolean and Number type as value in `TracingUtils.putAnnotation()`([#423](https://github.com/awslabs/aws-lambda-powertools-java/pull/432)). * **Logging**: API to remove any additional custom key from logger entry using `LoggingUtils.removeKeys()`([#395](https://github.com/awslabs/aws-lambda-powertools-java/pull/395)). -## Maintenance +### Maintenance * **deps**: Bump third party dependencies to the latest versions. diff --git a/README.md b/README.md index 378b580ac..5bf3c7977 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,17 @@ Powertools is available in Maven Central. You can use your favourite dependency software.amazon.lambda powertools-tracing - 1.13.0 + 1.14.0 software.amazon.lambda powertools-logging - 1.13.0 + 1.14.0 software.amazon.lambda powertools-metrics - 1.13.0 + 1.14.0 ... diff --git a/mkdocs.yml b/mkdocs.yml index aef84c1e6..4a72e1cc4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -81,7 +81,7 @@ extra_javascript: extra: powertools: - version: 1.13.0 + version: 1.14.0 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index 76f72b2d9..37bae3e0a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.13.0 + 1.14.0 pom AWS Lambda Powertools for Java library Parent diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 823f04a99..4946d9424 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 06a6768d5..fea692bfd 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library Core diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 51230dc42..020108fd5 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 1.13.0 + 1.14.0 powertools-idempotency diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 23ae2c345..58f239263 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index d382588da..3f409e949 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 313048a1e..66eedce1e 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 4e52c3928..06cb1b133 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 79a7b40d2..63aa72606 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 8f6e8ba69..153605bc9 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 5f810ffb0..3ca97bebc 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index e46d01294..7934517e4 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ powertools-parent software.amazon.lambda - 1.13.0 + 1.14.0 AWS Lambda Powertools for Java validation library From 4fe941aefb99ef50db8b62434bea85afc5fe74ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 09:04:23 +0100 Subject: [PATCH 0249/1008] build(deps): bump maven-javadoc-plugin from 3.4.1 to 3.5.0 (#1054) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.1 to 3.5.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 37bae3e0a..5ca158654 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 0.8.8 2.7 1.6.8 - 3.4.1 + 3.5.0 3.2.1 3.0.1 5.9.2 From 53c07ff346b37fd1f0f13f3dc362513b79b8230f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 09:06:06 +0100 Subject: [PATCH 0250/1008] build(deps): bump json-schema-validator from 1.0.76 to 1.0.77 (#1053) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.76 to 1.0.77. --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 7934517e4..14c4e4f9e 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ com.networknt json-schema-validator - 1.0.76 + 1.0.77 com.amazonaws From 596cdac0eef47b797c84eb72f3a39f5444390902 Mon Sep 17 00:00:00 2001 From: Fernanda Machado Date: Tue, 21 Feb 2023 09:30:14 +0100 Subject: [PATCH 0251/1008] chore(governance): update issue templates (#1062) --- .github/ISSUE_TEMPLATE/config.yml | 5 ++ .../documentation-improvements.md | 17 ----- .../documentation_improvements.yml | 50 ++++++++++++++ .github/ISSUE_TEMPLATE/maintenance.yml | 67 +++++++++++++++++++ .github/ISSUE_TEMPLATE/share_your_work.yml | 56 ++++++++++++++++ 5 files changed, 178 insertions(+), 17 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/documentation-improvements.md create mode 100644 .github/ISSUE_TEMPLATE/documentation_improvements.yml create mode 100644 .github/ISSUE_TEMPLATE/maintenance.yml create mode 100644 .github/ISSUE_TEMPLATE/share_your_work.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..100078541 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/awslabs/aws-lambda-powertools-java/discussions/new + about: Ask a general question about Lambda Powertools diff --git a/.github/ISSUE_TEMPLATE/documentation-improvements.md b/.github/ISSUE_TEMPLATE/documentation-improvements.md deleted file mode 100644 index 8341ae4e0..000000000 --- a/.github/ISSUE_TEMPLATE/documentation-improvements.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Documentation improvements -about: Suggest a documentation update -title: '' -labels: documentation -assignees: '' - ---- - -**What were you initially searching for in the docs?** - - -**Is this related to an existing part of the documentation? Please share a link** - -**Describe how we could make it clearer** - -**If you have a proposed update, please share it here** diff --git a/.github/ISSUE_TEMPLATE/documentation_improvements.yml b/.github/ISSUE_TEMPLATE/documentation_improvements.yml new file mode 100644 index 000000000..e750d5192 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_improvements.yml @@ -0,0 +1,50 @@ +name: Documentation improvements +description: Suggest a documentation update to improve everyone's experience +title: "Docs: TITLE" +labels: ["documentation", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for helping us improve everyone's experience. We review documentation updates on a case by case basis. + - type: textarea + id: search_area + attributes: + label: What were you searching in the docs? + description: Please help us understand how you looked for information that was either unclear or not available + validations: + required: true + - type: input + id: area + attributes: + label: Is this related to an existing documentation section? + description: Please share a link, if applicable + validations: + required: false + - type: textarea + id: idea + attributes: + label: How can we improve? + description: Please share your thoughts on how we can improve this experience + validations: + required: true + - type: textarea + id: suggestion + attributes: + label: Got a suggestion in mind? + description: Please suggest a proposed update + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: I understand the final update might be different from my proposed suggestion, or refused. + required: true + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml new file mode 100644 index 000000000..466feed7b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -0,0 +1,67 @@ +name: Maintenance +description: Suggest an activity to help address tech debt, governance, and anything internal +title: "Maintenance: TITLE" +labels: ["internal", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to help us improve operational excellence. + + *Future readers*: Please react with 👍 and your use case to help us understand customer demand. + - type: textarea + id: activity + attributes: + label: Summary + description: Please provide an overview in one or two paragraphs + validations: + required: true + - 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: + - Automation + - Governance + - Tests + - Tracer + - Logger + - Metrics + - Parameters + - SQS Large Message Handling + - SQS Batch Processing + - Validation + - Idempotency + - Custom Resources + - Serialization + - Other + - type: textarea + id: suggestion + attributes: + label: Solution + description: If available, please share what a good solution would look like + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This request meets [Lambda Powertools Tenets](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) + required: true + - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-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. diff --git a/.github/ISSUE_TEMPLATE/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml new file mode 100644 index 000000000..974aec87b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/share_your_work.yml @@ -0,0 +1,56 @@ +name: I Made This (showcase your work) +description: Share what you did with Powertools 💞💞. Blog post, workshops, presentation, sample apps, etc. +title: "[I Made This]: " +labels: ["community-content"] +body: + - type: markdown + attributes: + value: Thank you for helping spread the word out on Powertools, truly! + - type: input + id: content + attributes: + label: Link to your material + description: | + Please share the original link to your material. + + *Note: Short links will be expanded when added to Powertools documentation* + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Describe in one paragraph what's in it for them (readers) + validations: + required: true + - type: input + id: author + attributes: + label: Preferred contact + description: What's your preferred contact? We'll list it next to this content + validations: + required: true + - type: input + id: author-social + attributes: + label: (Optional) Social Network + description: If different from preferred contact, what's your preferred contact for social interactions? + validations: + required: false + - type: textarea + id: notes + attributes: + label: (Optional) Additional notes + description: | + Any notes you might want to share with us related to this material. + + *Note: These notes are explicitly to Powertools maintainers. It will not be added to the community resources page.* + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: I understand this content may be removed from Powertools documentation if it doesn't conform with the [Code of Conduct](https://aws.github.io/code-of-conduct) + required: true From e72e82c53c4c5d6811a9d651434051e3f755ff6d Mon Sep 17 00:00:00 2001 From: Ahmed Kamel <kamel@amazon.com> Date: Tue, 21 Feb 2023 09:57:45 +0000 Subject: [PATCH 0252/1008] Update how a request handler method is identified (#1058) - Stop relying on handler method name - Leverage placedOnRequestHandler and placedOnStreamHandler instead --- .../core/internal/LambdaHandlerProcessor.java | 4 +- .../internal/LambdaHandlerProcessorTest.java | 59 +++++++++++++------ .../internal/IdempotentAspect.java | 3 +- .../metrics/internal/LambdaMetricsAspect.java | 6 +- .../tracing/internal/LambdaTracingAspect.java | 11 +--- .../validation/internal/ValidationAspect.java | 4 +- 6 files changed, 46 insertions(+), 41 deletions(-) diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index ffe889db9..1cff812b8 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -37,9 +37,7 @@ private LambdaHandlerProcessor() { } public static boolean isHandlerMethod(final ProceedingJoinPoint pjp) { - return "handleRequest".equals(pjp.getSignature().getName()) || - // https://docs.aws.amazon.com/codeguru/latest/profiler-ug/lambda-custom.html - "requestHandler".equals(pjp.getSignature().getName()); + return placedOnRequestHandler(pjp) || placedOnStreamHandler(pjp); } public static boolean placedOnRequestHandler(final ProceedingJoinPoint pjp) { diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java index 18adae32e..6ed8b4160 100644 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java +++ b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java @@ -1,9 +1,15 @@ package software.amazon.lambda.powertools.core.internal; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.junit.jupiter.api.Test; +import java.io.InputStream; +import java.io.OutputStream; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -11,35 +17,50 @@ class LambdaHandlerProcessorTest { @Test - void shouldTreatProfilerHandlerMethodAsValid() { - ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); - Signature signature = mock(Signature.class); - when(signature.getName()).thenReturn("requestHandler"); - when(pjpMock.getSignature()).thenReturn(signature); + void isHandlerMethod_shouldRecognizeRequestHandler() { + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(); - assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)) - .isTrue(); + assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); } @Test - void shouldTreatDefaultHandlerMethodAsValid() { - ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); - Signature signature = mock(Signature.class); - when(signature.getName()).thenReturn("handleRequest"); - when(pjpMock.getSignature()).thenReturn(signature); + void isHandlerMethod_shouldRecognizeRequestStreamHandler() { + ProceedingJoinPoint pjpMock = mockRequestStreamHandlerPjp(); - assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)) - .isTrue(); + assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); } @Test - void shouldNotTreatOtherMethodNamesAsValidHandlerMethod() { - ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + void placedOnRequestHandler_shouldRecognizeRequestHandler() { + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(); + + assertThat(LambdaHandlerProcessor.placedOnRequestHandler(pjpMock)).isTrue(); + } + + @Test + void placedOnStreamHandler_shouldRecognizeRequestStreamHandler() { + ProceedingJoinPoint pjpMock = mockRequestStreamHandlerPjp(); + + assertThat(LambdaHandlerProcessor.placedOnStreamHandler(pjpMock)).isTrue(); + } + + private static ProceedingJoinPoint mockRequestHandlerPjp() { Signature signature = mock(Signature.class); - when(signature.getName()).thenReturn("handleRequestInvalid"); + when(signature.getDeclaringType()).thenReturn(RequestHandler.class); + ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + Object[] args = {new Object(), mock(Context.class)}; + when(pjpMock.getArgs()).thenReturn(args); when(pjpMock.getSignature()).thenReturn(signature); + return pjpMock; + } - assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)) - .isFalse(); + private static ProceedingJoinPoint mockRequestStreamHandlerPjp() { + Signature signature = mock(Signature.class); + when(signature.getDeclaringType()).thenReturn(RequestStreamHandler.class); + ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + when(pjpMock.getArgs()).thenReturn(args); + when(pjpMock.getSignature()).thenReturn(signature); + return pjpMock; } } \ No newline at end of file diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index 8bc3c8c8a..dc2703e64 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -30,7 +30,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; /** @@ -58,7 +57,7 @@ public Object around(ProceedingJoinPoint pjp, throw new IdempotencyConfigurationException("The annotated method doesn't return anything. Unable to perform idempotency on void return type"); } - boolean isHandler = (isHandlerMethod(pjp) && placedOnRequestHandler(pjp)); + boolean isHandler = placedOnRequestHandler(pjp); JsonNode payload = getPayload(pjp, method, isHandler); if (payload == null) { throw new IdempotencyConfigurationException("Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); 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 09fd5d87d..927359fc5 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 @@ -22,8 +22,6 @@ 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.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.metrics.MetricsUtils.hasDefaultDimension; import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; @@ -44,9 +42,7 @@ public Object around(ProceedingJoinPoint pjp, Metrics metrics) throws Throwable { Object[] proceedArgs = pjp.getArgs(); - if (isHandlerMethod(pjp) - && (placedOnRequestHandler(pjp) - || placedOnStreamHandler(pjp))) { + if (isHandlerMethod(pjp)) { MetricsLogger logger = metricsLogger(); 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 19fc1b038..26feec66b 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 @@ -26,8 +26,6 @@ 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.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.tracing.TracingUtils.objectMapper; @@ -48,7 +46,7 @@ public Object around(ProceedingJoinPoint pjp, () -> "## " + pjp.getSignature().getName())); segment.setNamespace(namespace(tracing)); - if (placedOnHandlerMethod(pjp)) { + if (isHandlerMethod(pjp)) { segment.putAnnotation("ColdStart", isColdStart()); segment.putAnnotation("Service", namespace(tracing)); } @@ -62,7 +60,7 @@ public Object around(ProceedingJoinPoint pjp, segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", null != objectMapper() ? objectMapper().writeValueAsString(methodReturn): methodReturn); } - if (placedOnHandlerMethod(pjp)) { + if (isHandlerMethod(pjp)) { coldStartDone(); } @@ -116,11 +114,6 @@ private String namespace(Tracing powerToolsTracing) { return powerToolsTracing.namespace().isEmpty() ? serviceName() : powerToolsTracing.namespace(); } - private boolean placedOnHandlerMethod(ProceedingJoinPoint pjp) { - return isHandlerMethod(pjp) - && (placedOnRequestHandler(pjp) || placedOnStreamHandler(pjp)); - } - private boolean environmentVariable(String key) { return Boolean.parseBoolean(SystemWrapper.getenv(key)); } 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 a9d43271b..be19a50a0 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 @@ -26,7 +26,6 @@ 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.isHandlerMethod; import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; @@ -55,8 +54,7 @@ public Object around(ProceedingJoinPoint pjp, ValidationConfig.get().setSchemaVersion(validation.schemaVersion()); } - if (isHandlerMethod(pjp) - && placedOnRequestHandler(pjp)) { + if (placedOnRequestHandler(pjp)) { validationNeeded = true; if (!validation.inboundSchema().isEmpty()) { From 33fe108f5e6e9a6e9760cc404a54321e583b57db Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:51:53 +0100 Subject: [PATCH 0253/1008] [feat] Import examples from aws-samples/aws-lambda-powertools-examples (#1051) --- .github/workflows/build.yml | 4 + README.md | 5 +- examples/.gitignore | 2 + examples/README.md | 5 + examples/pom.xml | 35 ++++ examples/powertools-examples-core/.gitignore | 0 examples/powertools-examples-core/README.md | 138 ++++++++++++++ .../events/event.json | 63 +++++++ examples/powertools-examples-core/pom.xml | 121 ++++++++++++ .../src/main/java/helloworld/App.java | 115 +++++++++++ .../src/main/java/helloworld/AppStream.java | 25 +++ .../src/main/resources/log4j2.xml | 16 ++ .../src/test/java/helloworld/AppTest.java | 43 +++++ .../powertools-examples-core/template.yaml | 66 +++++++ .../powertools-examples-idempotency/README.md | 21 +++ .../powertools-examples-idempotency/pom.xml | 178 ++++++++++++++++++ .../src/main/java/helloworld/App.java | 92 +++++++++ .../src/test/java/helloworld/AppTest.java | 86 +++++++++ .../src/test/resources/event.json | 63 +++++++ .../template.yaml | 59 ++++++ .../powertools-examples-parameters/README.md | 21 +++ .../powertools-examples-parameters/pom.xml | 90 +++++++++ .../java/org/demo/parameters/MyObject.java | 34 ++++ .../demo/parameters/ParametersFunction.java | 81 ++++++++ .../src/main/resources/log4j2.xml | 16 ++ .../template.yaml | 97 ++++++++++ .../README.md | 21 +++ .../events/APIGatewayEvent.json | 63 +++++++ .../events/SQSEvent.json | 39 ++++ .../powertools-examples-serialization/pom.xml | 67 +++++++ ...GatewayRequestDeserializationFunction.java | 36 ++++ .../java/org/demo/serialization/Product.java | 49 +++++ .../SQSEventDeserializationFunction.java | 27 +++ .../src/main/resources/log4j2.xml | 16 ++ ...wayRequestDeserializationFunctionTest.java | 35 ++++ .../SQSEventDeserializationFunctionTest.java | 47 +++++ .../template.yaml | 63 +++++++ examples/powertools-examples-sqs/README.md | 3 + .../powertools-examples-sqs/events/event.json | 63 +++++++ examples/powertools-examples-sqs/pom.xml | 122 ++++++++++++ .../java/org/demo/sqs/SqsMessageSender.java | 79 ++++++++ .../src/main/java/org/demo/sqs/SqsPoller.java | 61 ++++++ .../src/main/resources/log4j2.xml | 16 ++ .../powertools-examples-sqs/template.yaml | 148 +++++++++++++++ .../powertools-examples-validation/README.md | 21 +++ .../events/event.json | 63 +++++++ .../powertools-examples-validation/pom.xml | 86 +++++++++ .../demo/validation/InboundValidation.java | 51 +++++ .../src/main/resources/schema.json | 55 ++++++ .../validation/InboundValidationTest.java | 51 +++++ .../template.yaml | 33 ++++ examples/spotbugs-exclude.xml | 20 ++ pom.xml | 5 +- spotbugs-exclude.xml | 9 + 54 files changed, 2821 insertions(+), 4 deletions(-) create mode 100644 examples/.gitignore create mode 100644 examples/README.md create mode 100644 examples/pom.xml create mode 100644 examples/powertools-examples-core/.gitignore create mode 100644 examples/powertools-examples-core/README.md create mode 100644 examples/powertools-examples-core/events/event.json create mode 100644 examples/powertools-examples-core/pom.xml create mode 100644 examples/powertools-examples-core/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core/src/test/java/helloworld/AppTest.java create mode 100644 examples/powertools-examples-core/template.yaml create mode 100644 examples/powertools-examples-idempotency/README.md create mode 100644 examples/powertools-examples-idempotency/pom.xml create mode 100644 examples/powertools-examples-idempotency/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java create mode 100644 examples/powertools-examples-idempotency/src/test/resources/event.json create mode 100644 examples/powertools-examples-idempotency/template.yaml create mode 100644 examples/powertools-examples-parameters/README.md create mode 100644 examples/powertools-examples-parameters/pom.xml create mode 100644 examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java create mode 100644 examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java create mode 100644 examples/powertools-examples-parameters/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-parameters/template.yaml create mode 100644 examples/powertools-examples-serialization/README.md create mode 100644 examples/powertools-examples-serialization/events/APIGatewayEvent.json create mode 100644 examples/powertools-examples-serialization/events/SQSEvent.json create mode 100644 examples/powertools-examples-serialization/pom.xml create mode 100644 examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java create mode 100644 examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java create mode 100644 examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java create mode 100644 examples/powertools-examples-serialization/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java create mode 100644 examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java create mode 100644 examples/powertools-examples-serialization/template.yaml create mode 100644 examples/powertools-examples-sqs/README.md create mode 100644 examples/powertools-examples-sqs/events/event.json create mode 100644 examples/powertools-examples-sqs/pom.xml create mode 100644 examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java create mode 100644 examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java create mode 100644 examples/powertools-examples-sqs/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-sqs/template.yaml create mode 100644 examples/powertools-examples-validation/README.md create mode 100644 examples/powertools-examples-validation/events/event.json create mode 100644 examples/powertools-examples-validation/pom.xml create mode 100644 examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java create mode 100644 examples/powertools-examples-validation/src/main/resources/schema.json create mode 100644 examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java create mode 100644 examples/powertools-examples-validation/template.yaml create mode 100644 examples/spotbugs-exclude.xml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66ea80739..3210dad84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,9 @@ on: - 'powertools-parameters/**' - 'powertools-metrics/**' - 'powertools-test-suite/**' + - 'examples/**' - 'pom.xml' + - 'examples/pom.xml' - '.github/workflows/**' push: branches: @@ -33,7 +35,9 @@ on: - 'powertools-parameters/**' - 'powertools-metrics/**' - 'powertools-test-suite/**' + - 'examples/**' - 'pom.xml' + - 'examples/pom.xml' - '.github/workflows/**' jobs: build: diff --git a/README.md b/README.md index 5bf3c7977..addb0f631 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,9 @@ And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambd ## Example -See **[example](https://github.com/aws-samples/aws-lambda-powertools-examples/tree/main/java)** for example project 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](https://github.com/aws-samples/aws-lambda-powertools-examples/blob/main/CONTRIBUTING.md#security-issue-notifications). +See the **[examples](examples)** directory 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). ## Credits diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 000000000..79e044a40 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,2 @@ +dependency-reduced-pom.xml +.aws-sam diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..b5a0da8cd --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +## aws-lambda-powertools-examples + +This directory holds example projects demoing different components of the Lambda Powertools for Java. + + diff --git a/examples/pom.xml b/examples/pom.xml new file mode 100644 index 000000000..c61e858c5 --- /dev/null +++ b/examples/pom.xml @@ -0,0 +1,35 @@ +<?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> + + <artifactId>powertools-examples</artifactId> + <packaging>pom</packaging> + + <parent> + <artifactId>powertools-parent</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + <name>AWS Lambda Powertools for Java library Examples</name> + <description> + A suite of examples accompanying for AWS Lambda Powertools. + </description> + + <modules> + <module>powertools-examples-core</module> + <module>powertools-examples-idempotency</module> + <module>powertools-examples-parameters</module> + <module>powertools-examples-serialization</module> + <module>powertools-examples-sqs</module> + <module>powertools-examples-validation</module> + </modules> + + <!-- Don't deploy the examples --> + <properties> + <maven.deploy.skip>true</maven.deploy.skip> + </properties> + +</project> \ No newline at end of file diff --git a/examples/powertools-examples-core/.gitignore b/examples/powertools-examples-core/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md new file mode 100644 index 000000000..f4bdc399c --- /dev/null +++ b/examples/powertools-examples-core/README.md @@ -0,0 +1,138 @@ +# CoreUtilities + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes [Lambda Powertools for operational best practices](https://github.com/awslabs/aws-lambda-powertools-java), and the following files and folders. + +- HelloWorldFunction/src/main - Code for the application's Lambda function. +- events - Invocation events that you can use to invoke the function. +- HelloWorldFunction/src/test - Unit tests for the application code. +- template.yaml - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +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) + +## Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java11 - [Install the 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) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +Coreutilities$ sam build +Coreutilities$ sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modified IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +## Use the SAM CLI to build and test locally + +Build your application with the `sam build` command. + +```bash +Coreutilities$ sam build +``` + +The SAM CLI installs dependencies defined in `HelloWorldFunction/pom.xml`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +Coreutilities$ sam local invoke HelloWorldFunction --event events/event.json +``` + +The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. + +```bash +Coreutilities$ sam local start-api +Coreutilities$ curl http://localhost:3000/ +``` + +The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get +``` + +## Add a resource to your application +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +Coreutilities$ sam logs -n HelloWorldFunction --stack-name <Name-of-your-deployed-stack> --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `HelloWorldFunction/src/test` folder in this project. + +```bash +Coreutilities$ cd HelloWorldFunction +HelloWorldFunction$ mvn test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +aws cloudformation delete-stack --stack-name <Name-of-your-deployed-stack> +``` + +# Appendix + +## Powertools + +**Tracing** + +[Tracing utility](https://awslabs.github.io/aws-lambda-powertools-java/core/tracing/) provides functionality to reduce the overhead of performing common tracing tasks. It traces the execution of this sample code including the response and exceptions as tracing metadata - You can visualize them in AWS X-Ray. + +**Logger** + +[Logging utility](https://awslabs.github.io/aws-lambda-powertools-java/core/logging/) creates an opinionated application Logger with structured logging as the output, dynamically samples a percentage (samplingRate) of your logs in DEBUG mode for concurrent invocations, log incoming events as your function is invoked, and injects key information from Lambda context object into your Logger - You can visualize them in Amazon CloudWatch Logs. + +**Metrics** + +[Metrics utility](https://awslabs.github.io/aws-lambda-powertools-java/core/metrics/) captures cold start metric of your Lambda invocation, and could add additional metrics to help you understand your application KPIs - You can visualize them in Amazon CloudWatch. + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Check the [AWS Lambda Powertools Java](https://awslabs.github.io/aws-lambda-powertools-java/) for more information on how to use and configure such tools + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/examples/powertools-examples-core/events/event.json b/examples/powertools-examples-core/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-core/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml new file mode 100644 index 000000000..86f3afd0d --- /dev/null +++ b/examples/powertools-examples-core/pom.xml @@ -0,0 +1,121 @@ +<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> + <artifactId>powertools-examples-core</artifactId> + <packaging>jar</packaging> + <name>AWS Lambda Powertools for Java library Examples - Core</name> + + <parent> + <artifactId>powertools-examples</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + <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.0</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>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.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> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.2.4</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> +</project> diff --git a/examples/powertools-examples-core/src/main/java/helloworld/App.java b/examples/powertools-examples-core/src/main/java/helloworld/App.java new file mode 100644 index 000000000..b45440114 --- /dev/null +++ b/examples/powertools-examples-core/src/main/java/helloworld/App.java @@ -0,0 +1,115 @@ +package helloworld; + +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 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.AWSXRay; +import com.amazonaws.xray.entities.Entity; +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 software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.TracingUtils; +import software.amazon.lambda.powertools.tracing.Tracing; + +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 static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private final static Logger log = LogManager.getLogger(App.class); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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); + }); + + threadOption1(); + + threadOption2(); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (IOException | InterruptedException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + private void threadOption1() throws InterruptedException { + final Entity traceEntity = AWSXRay.getTraceEntity(); + assert traceEntity != null; + traceEntity.run(new Thread(this::log)); + } + + private void threadOption2() throws InterruptedException { + Entity traceEntity = AWSXRay.getTraceEntity(); + Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> { + String var = "somethingToProcess"; + log.info("inside threaded logging inline {}", var); + })); + anotherThread.start(); + anotherThread.join(); + } + + @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/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..aed048eef --- /dev/null +++ b/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java @@ -0,0 +1,25 @@ +package helloworld; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +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; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/src/main/resources/log4j2.xml b/examples/powertools-examples-core/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e1fd14cea --- /dev/null +++ b/examples/powertools-examples-core/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> \ No newline at end of file diff --git a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..b584ee944 --- /dev/null +++ b/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java @@ -0,0 +1,43 @@ +package helloworld; + +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; + +import static org.junit.Assert.*; + +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/template.yaml b/examples/powertools-examples-core/template.yaml new file mode 100644 index 000000000..3ca7f0ff2 --- /dev/null +++ b/examples/powertools-examples-core/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 env vars: https://awslabs.github.io/aws-lambda-powertools-python/#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 + 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-idempotency/README.md b/examples/powertools-examples-idempotency/README.md new file mode 100644 index 000000000..a919f0c70 --- /dev/null +++ b/examples/powertools-examples-idempotency/README.md @@ -0,0 +1,21 @@ +# Idempotency + +This project contains an example of Lambda function using the idempotency module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/idempotency/). + +## Deploy the sample application + +This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java11 - [Install the 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) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +Coreutilities$ sam build +Coreutilities$ sam deploy --guided +``` diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml new file mode 100644 index 000000000..438933178 --- /dev/null +++ b/examples/powertools-examples-idempotency/pom.xml @@ -0,0 +1,178 @@ +<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> + <artifactId>powertools-examples-idempotency</artifactId> + <packaging>jar</packaging> + <name>AWS Lambda Powertools for Java library Examples - Idempotency</name> + + <parent> + <artifactId>powertools-examples</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + <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.0</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.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>DynamoDBLocal</artifactId> + <version>1.21.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>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.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-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.0.0-M5</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.2.4</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> + <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/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java new file mode 100644 index 000000000..f26877c34 --- /dev/null +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -0,0 +1,92 @@ +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 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.logging.Logging; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +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; + +public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private final static Logger log = LogManager.getLogger(App.class); + + public App() { + this(null); + } + + 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()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build() + ).configure(); + } + + /** + * Try with: + * <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) + 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 { + 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); + } + } + + // we could also put the @Idempotent annotation here, but using it on the handler avoids executing the handler (cost reduction). + // Use it on other methods to handle multiple items (with SQS batch processing for example) + 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())); + } + } +} \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..7a5304e36 --- /dev/null +++ b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java @@ -0,0 +1,86 @@ +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 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.*; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; + +public class AppTest { + @Mock + private Context context; + private App app; + private static DynamoDbClient client; + + @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-idempotency/src/test/resources/event.json b/examples/powertools-examples-idempotency/src/test/resources/event.json new file mode 100644 index 000000000..fd7f5ace7 --- /dev/null +++ b/examples/powertools-examples-idempotency/src/test/resources/event.json @@ -0,0 +1,63 @@ +{ + "body": "{\"address\": \"https://checkip.amazonaws.com\"}", + "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-idempotency/template.yaml b/examples/powertools-examples-idempotency/template.yaml new file mode 100644 index 000000000..2d5e5bc56 --- /dev/null +++ b/examples/powertools-examples-idempotency/template.yaml @@ -0,0 +1,59 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + Idempotency demo + +Globals: + Function: + Timeout: 20 + Runtime: java11 + 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 + 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-parameters/README.md b/examples/powertools-examples-parameters/README.md new file mode 100644 index 000000000..c03f407c0 --- /dev/null +++ b/examples/powertools-examples-parameters/README.md @@ -0,0 +1,21 @@ +# Parameters + +This project contains an example of Lambda function using the parameters module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters/). + +## Deploy the sample application + +This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java11 - [Install the 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) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml new file mode 100644 index 000000000..f3797c948 --- /dev/null +++ b/examples/powertools-examples-parameters/pom.xml @@ -0,0 +1,90 @@ +<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> + <artifactId>powertools-examples-parameters</artifactId> + <packaging>jar</packaging> + <name>AWS Lambda Powertools for Java library Examples - Parameters</name> + + <parent> + <artifactId>powertools-examples</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + <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.0</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.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.9.2</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 2.22.0 or higher --> + <version>2.22.0</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.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-parameters</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java new file mode 100644 index 000000000..2cf145284 --- /dev/null +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java @@ -0,0 +1,34 @@ +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/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java new file mode 100644 index 000000000..7f41e020e --- /dev/null +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java @@ -0,0 +1,81 @@ +package org.demo.parameters; + +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.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 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 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; + +public class ParametersFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + private final static Logger log = LogManager.getLogger(); + + SSMProvider ssmProvider = ParamManager.getSsmProvider(); + SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); + + String simpleValue = ssmProvider.defaultMaxAge(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/src/main/resources/log4j2.xml b/examples/powertools-examples-parameters/src/main/resources/log4j2.xml new file mode 100644 index 000000000..033da8a11 --- /dev/null +++ b/examples/powertools-examples-parameters/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"> + <LambdaJsonLayout compact="true" eventEol="true"/> + </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/template.yaml new file mode 100644 index 000000000..052cfcdc7 --- /dev/null +++ b/examples/powertools-examples-parameters/template.yaml @@ -0,0 +1,97 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + validation demo + +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active + + +Resources: + ParametersFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.parameters.ParametersFunction::handleRequest + MemorySize: 512 + Tracing: Active + 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-serialization/README.md b/examples/powertools-examples-serialization/README.md new file mode 100644 index 000000000..68c4b4ae2 --- /dev/null +++ b/examples/powertools-examples-serialization/README.md @@ -0,0 +1,21 @@ +# Deserialization + +This project contains an example of Lambda function using the serialization utilities module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/serialization/). + +## Deploy the sample application + +This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java11 - [Install the 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) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` diff --git a/examples/powertools-examples-serialization/events/APIGatewayEvent.json b/examples/powertools-examples-serialization/events/APIGatewayEvent.json new file mode 100644 index 000000000..cb38a3429 --- /dev/null +++ b/examples/powertools-examples-serialization/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/events/SQSEvent.json b/examples/powertools-examples-serialization/events/SQSEvent.json new file mode 100644 index 000000000..9056d3ef2 --- /dev/null +++ b/examples/powertools-examples-serialization/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/pom.xml b/examples/powertools-examples-serialization/pom.xml new file mode 100644 index 000000000..f1d45f760 --- /dev/null +++ b/examples/powertools-examples-serialization/pom.xml @@ -0,0 +1,67 @@ +<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> + <artifactId>powertools-examples-serialization</artifactId> + <packaging>jar</packaging> + <name>AWS Lambda Powertools for Java library Examples - Serialization</name> + + <parent> + <artifactId>powertools-examples</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + <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.0</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.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.9.2</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 2.22.0 or higher --> + <version>2.22.0</version> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java new file mode 100644 index 000000000..8c33baed9 --- /dev/null +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java @@ -0,0 +1,36 @@ +package org.demo.serialization; + +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.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.HashMap; +import java.util.Map; + +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + + +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>() {{ + 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/src/main/java/org/demo/serialization/Product.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java new file mode 100644 index 000000000..fb94a99f8 --- /dev/null +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java @@ -0,0 +1,49 @@ +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/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java new file mode 100644 index 000000000..129fe0243 --- /dev/null +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java @@ -0,0 +1,27 @@ +package org.demo.serialization; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + + +public class SQSEventDeserializationFunction implements RequestHandler<SQSEvent, String> { + + private final static Logger LOGGER = LogManager.getLogger(SQSEventDeserializationFunction.class); + + 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(); + } +} + diff --git a/examples/powertools-examples-serialization/src/main/resources/log4j2.xml b/examples/powertools-examples-serialization/src/main/resources/log4j2.xml new file mode 100644 index 000000000..033da8a11 --- /dev/null +++ b/examples/powertools-examples-serialization/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"> + <LambdaJsonLayout compact="true" eventEol="true"/> + </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/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java new file mode 100644 index 000000000..5d5da7ecc --- /dev/null +++ b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java @@ -0,0 +1,35 @@ +package org.demo.serialization; + +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 static org.junit.jupiter.api.Assertions.assertEquals; + +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 new file mode 100644 index 000000000..6979a6868 --- /dev/null +++ b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java @@ -0,0 +1,47 @@ +package org.demo.serialization; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +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-serialization/template.yaml b/examples/powertools-examples-serialization/template.yaml new file mode 100644 index 000000000..539d2d615 --- /dev/null +++ b/examples/powertools-examples-serialization/template.yaml @@ -0,0 +1,63 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + serialization utils demo + +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active + + +Resources: + APIGatewayDeserializationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.serialization.APIGatewayRequestDeserializationFunction::handleRequest + 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: Function + Handler: org.demo.serialization.SQSEventDeserializationFunction::handleRequest + 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-sqs/README.md b/examples/powertools-examples-sqs/README.md new file mode 100644 index 000000000..2b6da65b5 --- /dev/null +++ b/examples/powertools-examples-sqs/README.md @@ -0,0 +1,3 @@ +## SqsBatchProcessingDemo + +Demos setup of SQS Batch processing via Powertools diff --git a/examples/powertools-examples-sqs/events/event.json b/examples/powertools-examples-sqs/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-sqs/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml new file mode 100644 index 000000000..0dbcabc53 --- /dev/null +++ b/examples/powertools-examples-sqs/pom.xml @@ -0,0 +1,122 @@ +<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> + <artifactId>powertools-examples-sqs</artifactId> + <packaging>jar</packaging> + <name>AWS Lambda Powertools for Java library Examples - SQS</name> + + <parent> + <artifactId>powertools-examples</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + <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.1</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.0</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.14.2</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13.2</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.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-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.4.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> +</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 new file mode 100644 index 000000000..b90c50654 --- /dev/null +++ b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java @@ -0,0 +1,79 @@ + +package org.demo.sqs; + +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 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; + +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 static java.util.stream.Collectors.toList; + +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 new file mode 100644 index 000000000..bf2b7bdfe --- /dev/null +++ b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java @@ -0,0 +1,61 @@ +package org.demo.sqs; + +import java.util.Random; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +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; +import java.security.SecureRandom; + +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + +/** + * Handler for requests to Lambda function. + */ +public class SqsPoller implements RequestHandler<SQSEvent, String> { + + Logger log = LogManager.getLogger(SqsPoller.class); + Random random = new SecureRandom(); + + static { + // https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/lambda-optimize-starttime.html + SqsUtils.overrideSqsClient(SqsClient.builder() + .httpClient(UrlConnectionHttpClient.create()) + .build()); + } + + @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/src/main/resources/log4j2.xml b/examples/powertools-examples-sqs/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e1fd14cea --- /dev/null +++ b/examples/powertools-examples-sqs/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> \ No newline at end of file diff --git a/examples/powertools-examples-sqs/template.yaml b/examples/powertools-examples-sqs/template.yaml new file mode 100644 index 000000000..ebf20831f --- /dev/null +++ b/examples/powertools-examples-sqs/template.yaml @@ -0,0 +1,148 @@ +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 env vars: https://awslabs.github.io/aws-lambda-powertools-python/#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 new file mode 100644 index 000000000..db631deff --- /dev/null +++ b/examples/powertools-examples-validation/README.md @@ -0,0 +1,21 @@ +# Validation + +This project contains an example of Lambda function using the validation module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/validation/). + +## Deploy the sample application + +This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java11 - [Install the 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) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` diff --git a/examples/powertools-examples-validation/events/event.json b/examples/powertools-examples-validation/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-validation/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml new file mode 100644 index 000000000..f7522303b --- /dev/null +++ b/examples/powertools-examples-validation/pom.xml @@ -0,0 +1,86 @@ +<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> + <artifactId>powertools-examples-validation</artifactId> + <packaging>jar</packaging> + <name>AWS Lambda Powertools for Java library Examples - Validation</name> + + <parent> + <artifactId>powertools-parent</artifactId> + <groupId>software.amazon.lambda</groupId> + <version>1.14.0</version> + </parent> + + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <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> + <artifactId>aws-lambda-java-core</artifactId> + <version>1.2.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.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.9.2</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 2.22.0 or higher --> + <version>2.22.0</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14.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-validation</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java b/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java new file mode 100644 index 000000000..89ec538c9 --- /dev/null +++ b/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java @@ -0,0 +1,51 @@ +package org.demo.validation; + +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; + +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; + +/** + * Request handler for Lambda function which demonstrates validation of request message. + */ +public class InboundValidation implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Validation(inboundSchema = "classpath:/schema.json") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + 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-validation/src/main/resources/schema.json b/examples/powertools-examples-validation/src/main/resources/schema.json new file mode 100644 index 000000000..f38272f2d --- /dev/null +++ b/examples/powertools-examples-validation/src/main/resources/schema.json @@ -0,0 +1,55 @@ +{ + "$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": [ + "id", + "name", + "price" + ], + "properties": { + "id": { + "$id": "#/properties/id", + "type": "integer", + "title": "Id of the product", + "description": "Unique identifier of the product", + "default": 0, + "examples": [ + 43242 + ] + }, + "name": { + "$id": "#/properties/name", + "type": "string", + "title": "Name of the product", + "description": "Explicit name of the product", + "minLength": 5, + "default": "", + "examples": [ + "FooBar XY" + ] + }, + "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/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 new file mode 100644 index 000000000..af47d3d87 --- /dev/null +++ b/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java @@ -0,0 +1,51 @@ +package org.demo.validation; + +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; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +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/powertools-examples-validation/template.yaml b/examples/powertools-examples-validation/template.yaml new file mode 100644 index 000000000..f18a9fb00 --- /dev/null +++ b/examples/powertools-examples-validation/template.yaml @@ -0,0 +1,33 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + validation demo + +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active + + +Resources: + ValidationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: org.demo.validation.InboundValidation::handleRequest + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: post + +Outputs: + Api: + Description: "API Gateway endpoint URL for Prod stage for Validation function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + Function: + Description: "Validation Lambda Function ARN" + Value: !GetAtt ValidationFunction.Arn \ No newline at end of file diff --git a/examples/spotbugs-exclude.xml b/examples/spotbugs-exclude.xml new file mode 100644 index 000000000..e33e65478 --- /dev/null +++ b/examples/spotbugs-exclude.xml @@ -0,0 +1,20 @@ +<!-- 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/pom.xml b/pom.xml index 5ca158654..121b3459e 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,8 @@ <module>powertools-validation</module> <module>powertools-test-suite</module> <module>powertools-cloudformation</module> - <module>powertools-idempotency</module> + <module>powertools-idempotency</module> + <module>examples</module> </modules> <scm> @@ -450,7 +451,7 @@ <profile> <id>build-with-spotbugs</id> <activation> - <activeByDefault>true</activeByDefault> + <activeByDefault>false</activeByDefault> </activation> <build> <plugins> diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index fe4194bfd..5a5e3bed8 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -152,4 +152,13 @@ <Class name="software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect"/> <Method name="setLogLevelBasedOnSamplingRate"/> </Match> + <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 From e3d154e680c8f533386b95f71f0f8da2b7f5c086 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 08:51:49 +0100 Subject: [PATCH 0254/1008] build(deps): bump aws.sdk.version from 2.20.0 to 2.20.9 (#1068) Bumps `aws.sdk.version` from 2.20.0 to 2.20.9. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 121b3459e..384ee8c79 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ <log4j.version>2.19.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.0</aws.sdk.version> + <aws.sdk.version>2.20.9</aws.sdk.version> <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 0a3d44daf425ab9cf7cef460082ba376b030548a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 08:53:11 +0100 Subject: [PATCH 0255/1008] build(deps): bump log4j.version from 2.19.0 to 2.20.0 (#1065) Bumps `log4j.version` from 2.19.0 to 2.20.0. Updates `log4j-core` from 2.19.0 to 2.20.0 Updates `log4j-slf4j-impl` from 2.19.0 to 2.20.0 Updates `log4j-api` from 2.19.0 to 2.20.0 Updates `log4j-layout-template-json` from 2.19.0 to 2.20.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 384ee8c79..da7bc64d3 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <log4j.version>2.19.0</log4j.version> + <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> <aws.sdk.version>2.20.9</aws.sdk.version> From ca1da462294e710ff2aad04e6f913dafb95266d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 08:53:42 +0100 Subject: [PATCH 0256/1008] build(deps): bump assertj-core from 3.23.1 to 3.24.2 (#1063) Bumps assertj-core from 3.23.1 to 3.24.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da7bc64d3..aca2162e1 100644 --- a/pom.xml +++ b/pom.xml @@ -263,7 +263,7 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.23.1</version> + <version>3.24.2</version> <scope>test</scope> </dependency> <dependency> From 63e1d16ee8683f01fdc58ddd88e48697f81ec8f0 Mon Sep 17 00:00:00 2001 From: Ahmed Kamel <humanzz@hotmail.com> Date: Thu, 23 Feb 2023 07:56:00 +0000 Subject: [PATCH 0257/1008] chore(metrics): deprecate withMetricLogger in favor of withMetricsLogger (#1060) chore(metrics): deprecated MetricsUtils.withMetricLogger in favor of MetricsUtils.withMetricsLogger --- .../powertools/metrics/MetricsUtils.java | 21 ++++++++++++--- .../powertools/metrics/MetricsLoggerTest.java | 27 +++++++++++++------ 2 files changed, 37 insertions(+), 11 deletions(-) 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 index d58dd52d6..8705c2da4 100644 --- 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 @@ -81,7 +81,7 @@ public static void withSingleMetric(final String name, final double value, final Unit unit, final Consumer<MetricsLogger> logger) { - withMetricLogger(metricsLogger -> { + withMetricsLogger(metricsLogger -> { metricsLogger.putMetric(name, value, unit); logger.accept(metricsLogger); }); @@ -103,7 +103,7 @@ public static void withSingleMetric(final String name, final Unit unit, final String namespace, final Consumer<MetricsLogger> logger) { - withMetricLogger(metricsLogger -> { + withMetricsLogger(metricsLogger -> { metricsLogger.setNamespace(namespace); metricsLogger.putMetric(name, value, unit); logger.accept(metricsLogger); @@ -118,7 +118,7 @@ public static void withSingleMetric(final String name, * * @param logger the MetricsLogger */ - public static void withMetricLogger(final Consumer<MetricsLogger> logger) { + public static void withMetricsLogger(final Consumer<MetricsLogger> logger) { MetricsLogger metricsLogger = logger(); try { @@ -130,6 +130,21 @@ public static void withMetricLogger(final Consumer<MetricsLogger> logger) { } } + /** + * 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); } 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 index 20f56c3e2..6ebf30e04 100644 --- 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 @@ -3,6 +3,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.Map; +import java.util.function.Consumer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -12,6 +13,7 @@ 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; @@ -124,13 +126,29 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { @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\""); - MetricsUtils.withMetricLogger(metricsLogger -> { + methodToTest.accept(metricsLogger -> { metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); metricsLogger.putMetric("Metric1", 1, Unit.COUNT); }); @@ -154,13 +172,6 @@ void metricsLoggerCaptureUtilityWithDefaultNameSpace() { } } - @Test - void shouldThrowExceptionWhenDefaultDimensionIsNull() { - assertThatNullPointerException() - .isThrownBy(() -> MetricsUtils.defaultDimensionSet(null)) - .withMessage("Null dimension set not allowed"); - } - private Map<String, Object> readAsJson(String s) { try { return mapper.readValue(s, Map.class); From 28113a17dfcd95fd8fd50e48c2cf2eff7c9b75d2 Mon Sep 17 00:00:00 2001 From: Fernanda Machado <machafer@amazon.com> Date: Fri, 24 Feb 2023 17:48:56 +0100 Subject: [PATCH 0258/1008] docs(plugin): fix mdocs and git revision plugin integration (#1066) * docs(plugin): fix mdocs and git revision plugin integration * trigger doc build when mkdocs and Makefile are modified --- .github/workflows/build-docs.yml | 7 ++++++- .github/workflows/docs.yml | 2 +- mkdocs.yml | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 7412255d3..97ad0a44e 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -6,11 +6,16 @@ on: - master paths: - 'docs/**' + - 'mkdocs.yml' + - 'Makefile' + push: branches: - master paths: - 'docs/**' + - 'mkdocs.yml' + - 'Makefile' jobs: docs: @@ -18,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: "3.8" - name: Capture branch and tag diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d00f64e91..d19c64b2d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: "3.8" - name: Capture branch and tag diff --git a/mkdocs.yml b/mkdocs.yml index 4a72e1cc4..10b07b21d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,6 +70,7 @@ markdown_extensions: copyright: Copyright © 2021 Amazon Web Services plugins: + - git-revision-date - search - macros From b062a7622d6845e1ea775a7dcd7c3658315e6dfa Mon Sep 17 00:00:00 2001 From: Mark Sailes <45629314+msailes@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:48:30 +0000 Subject: [PATCH 0259/1008] fix(cloudformation-module): Use physicalResourceId when not provided by custom resource (#1082) --- pom.xml | 12 + powertools-cloudformation/pom.xml | 10 + .../AbstractCustomResourceHandler.java | 7 +- .../CloudFormationResponse.java | 45 +++- .../powertools/cloudformation/Response.java | 54 +++- .../CloudFormationIntegrationTest.java | 247 ++++++++++++++++++ .../CloudFormationResponseTest.java | 26 +- .../NoPhysicalResourceIdSetHandler.java | 24 ++ .../PhysicalResourceIdSetHandler.java | 32 +++ .../RuntimeExceptionThrownHandler.java | 24 ++ .../src/test/resources/logback.xml | 11 + 11 files changed, 460 insertions(+), 32 deletions(-) create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java create mode 100644 powertools-cloudformation/src/test/resources/logback.xml diff --git a/pom.xml b/pom.xml index aca2162e1..91014ac87 100644 --- a/pom.xml +++ b/pom.xml @@ -283,6 +283,18 @@ <version>1.1.1</version> <scope>test</scope> </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.2.6</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.github.tomakehurst</groupId> + <artifactId>wiremock-jre8</artifactId> + <version>2.35.0</version> + <scope>test</scope> + </dependency> </dependencies> </dependencyManagement> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 4946d9424..5366cdc26 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -93,6 +93,16 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>com.github.tomakehurst</groupId> + <artifactId>wiremock-jre8</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java index 05f1a0f27..e721651a0 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java @@ -65,7 +65,12 @@ public final Response handleRequest(CloudFormationCustomResourceEvent event, Con } catch (CustomResourceResponseException rse) { LOG.error("Unable to generate response. Sending empty failure to {}", responseUrl, rse); try { - client.send(event, context, Response.failed()); + // If the customers code throws an exception, Powertools should respond in a way that doesn't + // change the CloudFormation resources. + // In the case of a Update or Delete, a failure is sent with the existing PhysicalResourceId + // indicating no change. + // In the case of a Create, null will be set and changed to the Lambda LogStreamName before sending. + client.send(event, context, Response.failed(event.getPhysicalResourceId())); } catch (Exception e) { // unable to generate response AND send the failure LOG.error("Unable to send failure response to {}.", responseUrl, e); 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 33cc533d2..39a86293b 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 @@ -6,6 +6,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.Header; import software.amazon.awssdk.http.HttpExecuteRequest; import software.amazon.awssdk.http.HttpExecuteResponse; @@ -32,6 +34,8 @@ */ class CloudFormationResponse { + private static final Logger LOG = LoggerFactory.getLogger(CloudFormationResponse.class); + /** * Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload * except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of @@ -53,14 +57,14 @@ static class ResponseBody { private final boolean noEcho; ResponseBody(CloudFormationCustomResourceEvent event, - Context context, Response.Status responseStatus, String physicalResourceId, - boolean noEcho) { + boolean noEcho, + String reason) { Objects.requireNonNull(event, "CloudFormationCustomResourceEvent cannot be null"); - Objects.requireNonNull(context, "Context cannot be null"); - this.physicalResourceId = physicalResourceId != null ? physicalResourceId : context.getLogStreamName(); - this.reason = "See the details in CloudWatch Log Stream: " + context.getLogStreamName(); + + this.physicalResourceId = physicalResourceId; + this.reason = reason; this.status = responseStatus == null ? Response.Status.SUCCESS.name() : responseStatus.name(); this.stackId = event.getStackId(); this.requestId = event.getRequestId(); @@ -111,6 +115,20 @@ ObjectNode toObjectNode(JsonNode dataNode) { } return node; } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("ResponseBody{"); + sb.append("status='").append(status).append('\''); + sb.append(", reason='").append(reason).append('\''); + sb.append(", physicalResourceId='").append(physicalResourceId).append('\''); + sb.append(", stackId='").append(stackId).append('\''); + sb.append(", requestId='").append(requestId).append('\''); + sb.append(", logicalResourceId='").append(logicalResourceId).append('\''); + sb.append(", noEcho=").append(noEcho); + sb.append('}'); + return sb.toString(); + } } private final SdkHttpClient client; @@ -195,23 +213,34 @@ protected Map<String, List<String>> headers(int contentLength) { /** * Returns the response body as an input stream, for supplying with the HTTP request to the custom resource. * + * If PhysicalResourceId is null at this point it will be replaced with the Lambda LogStreamName. + * * @throws CustomResourceResponseException if unable to generate the response stream */ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, Context context, Response resp) throws CustomResourceResponseException { try { + String reason = "See the details in CloudWatch Log Stream: " + context.getLogStreamName(); if (resp == null) { - ResponseBody body = new ResponseBody(event, context, Response.Status.SUCCESS, null, false); + String physicalResourceId = event.getPhysicalResourceId() != null? event.getPhysicalResourceId() : context.getLogStreamName(); + + ResponseBody body = new ResponseBody(event, Response.Status.SUCCESS, physicalResourceId, false, reason); + LOG.debug("ResponseBody: {}", body); ObjectNode node = body.toObjectNode(null); return new StringInputStream(node.toString()); } else { - ResponseBody body = new ResponseBody( - event, context, resp.getStatus(), resp.getPhysicalResourceId(), resp.isNoEcho()); + + String physicalResourceId = resp.getPhysicalResourceId() != null ? resp.getPhysicalResourceId() : + event.getPhysicalResourceId() != null? event.getPhysicalResourceId() : context.getLogStreamName(); + + ResponseBody body = new ResponseBody(event, resp.getStatus(), physicalResourceId, resp.isNoEcho(), reason); + LOG.debug("ResponseBody: {}", body); ObjectNode node = body.toObjectNode(resp.getJsonNode()); return new StringInputStream(node.toString()); } } catch (RuntimeException e) { + LOG.error(e.getMessage()); throw new CustomResourceResponseException("Unable to generate response body.", e); } } 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 3ae6b9296..6dd636a73 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 @@ -138,23 +138,71 @@ public static Builder builder() { } /** - * Creates an empty, failed Response. + * Creates a failed Response with no physicalResourceId set. Powertools will set the physicalResourceId to the + * Lambda LogStreamName * + * 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. + * + * @deprecated this method is not safe. Provide a physicalResourceId. * @return a failed Response with no value. */ + @Deprecated public static Response failed() { return new Response(null, Status.FAILED, null, false); } /** - * Creates an empty, successful Response. + * Creates a failed Response with a given physicalResourceId. * - * @return a failed Response with no value. + * @param physicalResourceId The value must be a non-empty string and must be identical for all responses for the + * same resource. + * 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 physicalResourceId */ + public static Response failed(String physicalResourceId) { + return new Response(null, Status.FAILED, physicalResourceId, false); + } + + /** + * Creates a successful Response with no physicalResourceId set. Powertools will set the physicalResourceId to the + * Lambda LogStreamName + * + * 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. + * + * @deprecated this method is not safe. Provide a physicalResourceId. + * @return a success Response with no physicalResourceId value. + */ + @Deprecated public static Response success() { return new Response(null, Status.SUCCESS, null, false); } + /** + * Creates a successful Response with a given physicalResourceId. + * + * @param physicalResourceId The value must be a non-empty string and must be identical for all responses for the + * same resource. + * 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 physicalResourceId + */ + public static Response success(String physicalResourceId) { + return new Response(null, Status.SUCCESS, physicalResourceId, false); + } + private final JsonNode jsonNode; private final Status status; private final String physicalResourceId; 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 new file mode 100644 index 000000000..06463308c --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java @@ -0,0 +1,247 @@ +package software.amazon.lambda.powertools.cloudformation; + +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 org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +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 java.util.UUID; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.assertj.core.api.Assertions.assertThat; + +@WireMockTest +public class CloudFormationIntegrationTest { + + public static final String PHYSICAL_RESOURCE_ID = UUID.randomUUID().toString(); + public static final String LOG_STREAM_NAME = "FakeLogStreamName"; + + @ParameterizedTest + @ValueSource(strings = {"Update", "Delete"}) + void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(put("/").willReturn(ok())); + + NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); + int httpPort = wmRuntimeInfo.getHttpPort(); + + CloudFormationCustomResourceEvent event = baseEvent(httpPort) + .withPhysicalResourceId(PHYSICAL_RESOURCE_ID) + .withRequestType(requestType) + .build(); + + handler.handleRequest(event, new FakeContext()); + + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]")) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"Update", "Delete"}) + void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDeleting(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(put("/").willReturn(ok())); + + RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); + int httpPort = wmRuntimeInfo.getHttpPort(); + + CloudFormationCustomResourceEvent event = baseEvent(httpPort) + .withPhysicalResourceId(PHYSICAL_RESOURCE_ID) + .withRequestType(requestType) + .build(); + + handler.handleRequest(event, new FakeContext()); + + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'FAILED')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]")) + ); + } + + @Test + void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(put("/").willReturn(ok())); + + RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); + CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) + .withRequestType("Create") + .build(); + handler.handleRequest(createEvent, new FakeContext()); + + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'FAILED')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + LOG_STREAM_NAME + "')]")) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"Update", "Delete"}) + void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAPhysicalResourceId(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(put("/").willReturn(ok())); + + NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); + int httpPort = wmRuntimeInfo.getHttpPort(); + + CloudFormationCustomResourceEvent event = baseEvent(httpPort) + .withPhysicalResourceId(PHYSICAL_RESOURCE_ID) + .withRequestType(requestType) + .build(); + + Response response = handler.handleRequest(event, new FakeContext()); + + assertThat(response).isNotNull(); + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + PHYSICAL_RESOURCE_ID + "')]")) + ); + } + + @Test + void createNewResourceBecausePhysicalResourceIdNotSetByCustomerOnCreate(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(put("/").willReturn(ok())); + + NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); + CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) + .withRequestType("Create") + .build(); + Response response = handler.handleRequest(createEvent, new FakeContext()); + + assertThat(response).isNotNull(); + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + LOG_STREAM_NAME + "')]")) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"Create", "Update", "Delete"}) + void physicalResourceIdReturnedFromSuccessToCloudformation(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + + String physicalResourceId = UUID.randomUUID().toString(); + + PhysicalResourceIdSetHandler handler = new PhysicalResourceIdSetHandler(physicalResourceId, true); + CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) + .withRequestType(requestType) + .build(); + Response response = handler.handleRequest(createEvent, new FakeContext()); + + assertThat(response).isNotNull(); + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'SUCCESS')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + physicalResourceId + "')]")) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"Create", "Update", "Delete"}) + void physicalResourceIdReturnedFromFailedToCloudformation(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + + String physicalResourceId = UUID.randomUUID().toString(); + + PhysicalResourceIdSetHandler handler = new PhysicalResourceIdSetHandler(physicalResourceId, false); + CloudFormationCustomResourceEvent createEvent = baseEvent(wmRuntimeInfo.getHttpPort()) + .withRequestType(requestType) + .build(); + Response response = handler.handleRequest(createEvent, new FakeContext()); + + assertThat(response).isNotNull(); + verify(putRequestedFor(urlPathMatching("/")) + .withRequestBody(matchingJsonPath("[?(@.Status == 'FAILED')]")) + .withRequestBody(matchingJsonPath("[?(@.PhysicalResourceId == '" + physicalResourceId + "')]")) + ); + } + + 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(); + } + + private static CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder baseEvent(int httpPort) { + CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = CloudFormationCustomResourceEvent.builder() + .withResponseUrl("http://localhost:" + httpPort + "/") + .withStackId("123") + .withRequestId("234") + .withLogicalResourceId("345"); + + return builder; + } + + 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 207eb9b7f..64c313695 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 @@ -103,20 +103,6 @@ void eventResponseUrlRequiredToSend() { .isInstanceOf(RuntimeException.class); } - @Test - void defaultPhysicalResponseIdIsLogStreamName() { - CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - when(event.getPhysicalResourceId()).thenReturn("This-Is-Ignored"); - - String logStreamName = "My-Log-Stream-Name"; - Context context = mock(Context.class); - when(context.getLogStreamName()).thenReturn(logStreamName); - - ResponseBody body = new ResponseBody( - event, context, Response.Status.SUCCESS, null, false); - assertThat(body.getPhysicalResourceId()).isEqualTo(logStreamName); - } - @Test void customPhysicalResponseId() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); @@ -127,7 +113,7 @@ void customPhysicalResponseId() { String customPhysicalResourceId = "Custom-Physical-Resource-ID"; ResponseBody body = new ResponseBody( - event, context, Response.Status.SUCCESS, customPhysicalResourceId, false); + event, Response.Status.SUCCESS, customPhysicalResourceId, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getPhysicalResourceId()).isEqualTo(customPhysicalResourceId); } @@ -136,7 +122,7 @@ void responseBodyWithNullDataNode() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); Context context = mock(Context.class); - ResponseBody responseBody = new ResponseBody(event, context, Response.Status.FAILED, null, true); + ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); String actualJson = responseBody.toObjectNode(null).toString(); String expectedJson = "{" + @@ -160,7 +146,7 @@ void responseBodyWithNonNullDataNode() { dataNode.put("foo", "bar"); dataNode.put("baz", 10); - ResponseBody responseBody = new ResponseBody(event, context, Response.Status.FAILED, null, true); + ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); String actualJson = responseBody.toObjectNode(dataNode).toString(); String expectedJson = "{" + @@ -182,7 +168,7 @@ void defaultStatusIsSuccess() { Context context = mock(Context.class); ResponseBody body = new ResponseBody( - event, context, null, null, false); + event, null, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getStatus()).isEqualTo("SUCCESS"); } @@ -192,7 +178,7 @@ void customStatus() { Context context = mock(Context.class); ResponseBody body = new ResponseBody( - event, context, Response.Status.FAILED, null, false); + event, Response.Status.FAILED, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getStatus()).isEqualTo("FAILED"); } @@ -205,7 +191,7 @@ void reasonIncludesLogStreamName() { when(context.getLogStreamName()).thenReturn(logStreamName); ResponseBody body = new ResponseBody( - event, context, Response.Status.SUCCESS, null, false); + event, Response.Status.SUCCESS, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getReason()).contains(logStreamName); } 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 new file mode 100644 index 000000000..68d057b54 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java @@ -0,0 +1,24 @@ +package software.amazon.lambda.powertools.cloudformation.handlers; + +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 NoPhysicalResourceIdSetHandler extends AbstractCustomResourceHandler { + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return Response.success(); + } + + @Override + protected Response update(CloudFormationCustomResourceEvent event, Context context) { + return Response.success(); + } + + @Override + protected Response delete(CloudFormationCustomResourceEvent event, Context context) { + return Response.success(); + } +} 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 new file mode 100644 index 000000000..51f520a3d --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java @@ -0,0 +1,32 @@ +package software.amazon.lambda.powertools.cloudformation.handlers; + +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 PhysicalResourceIdSetHandler extends AbstractCustomResourceHandler { + + private final String physicalResourceId; + private final boolean callsSucceed; + + public PhysicalResourceIdSetHandler(String physicalResourceId, boolean callsSucceed) { + this.physicalResourceId = physicalResourceId; + this.callsSucceed = callsSucceed; + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + } + + @Override + protected Response update(CloudFormationCustomResourceEvent event, Context context) { + return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + } + + @Override + protected Response delete(CloudFormationCustomResourceEvent event, Context context) { + return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + } +} 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 new file mode 100644 index 000000000..ee5be77b8 --- /dev/null +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java @@ -0,0 +1,24 @@ +package software.amazon.lambda.powertools.cloudformation.handlers; + +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 RuntimeExceptionThrownHandler extends AbstractCustomResourceHandler { + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + throw new RuntimeException("failure"); + } + + @Override + protected Response update(CloudFormationCustomResourceEvent event, Context context) { + throw new RuntimeException("failure"); + } + + @Override + protected Response delete(CloudFormationCustomResourceEvent event, Context context) { + throw new RuntimeException("failure"); + } +} diff --git a/powertools-cloudformation/src/test/resources/logback.xml b/powertools-cloudformation/src/test/resources/logback.xml new file mode 100644 index 000000000..8c752522e --- /dev/null +++ b/powertools-cloudformation/src/test/resources/logback.xml @@ -0,0 +1,11 @@ +<configuration> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + <logger name="software.amazon.lambda.powertools" level="DEBUG" /> + <root level="INFO"> + <appender-ref ref="STDOUT" /> + </root> +</configuration> \ No newline at end of file From 0de4c2d5bcb9f6276433b4cf9ff2de376e7e33a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 16:57:55 +0100 Subject: [PATCH 0260/1008] build(deps): bump spotbugs-maven-plugin from 4.7.3.0 to 4.7.3.2 (#1078) Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.7.3.0 to 4.7.3.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 91014ac87..bbd36486d 100644 --- a/pom.xml +++ b/pom.xml @@ -470,7 +470,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.0</version> + <version>4.7.3.2</version> <executions> <execution> <id>test</id> From aa0e76d16e54e525eac621ebc2d3e7d7f091e190 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 16:58:34 +0100 Subject: [PATCH 0261/1008] build(deps): bump maven-compiler-plugin from 3.10.1 to 3.11.0 (#1077) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bbd36486d..ac7146916 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ <lambda.core.version>1.2.2</lambda.core.version> <lambda.events.version>3.11.0</lambda.events.version> <lambda.serial.version>1.0.0</lambda.serial.version> - <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version> + <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version> <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version> From 48a3a3ebd2ed8aae909fb8e17e666a2ea5b626fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 16:59:51 +0100 Subject: [PATCH 0262/1008] build(deps): bump aws.sdk.version from 2.20.9 to 2.20.11 (#1072) Bumps `aws.sdk.version` from 2.20.9 to 2.20.11. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0dbcabc53..409f1c2ad 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.1</version> + <version>2.20.11</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index ac7146916..a4ae99072 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.9</aws.sdk.version> + <aws.sdk.version>2.20.11</aws.sdk.version> <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 5d7f2e6ffd9b18caa4464f36b2af2238f8dc82cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 17:00:54 +0100 Subject: [PATCH 0263/1008] build(deps): bump maven-shade-plugin from 3.2.4 to 3.4.1 (#1070) Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.4 to 3.4.1. --- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 86f3afd0d..640cab182 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -92,7 +92,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.2.4</version> + <version>3.4.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 438933178..ee151ce0f 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -142,7 +142,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.2.4</version> + <version>3.4.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 409f1c2ad..8abcfa56f 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -93,7 +93,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.4.0</version> + <version>3.4.1</version> <executions> <execution> <phase>package</phase> From f0c956260f59b687aedafb9514af18c590880e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:47:03 +0100 Subject: [PATCH 0264/1008] maintenance: send coverage (jacoco) to codecov (#1094) send coverage (jacoco) to codecov --- .github/workflows/build.yml | 11 ++++++-- README.md | 3 +- examples/pom.xml | 28 +++++++++++++++++++ .../powertools-examples-validation/pom.xml | 3 +- pom.xml | 17 ----------- 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3210dad84..db25e4164 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,8 +49,7 @@ jobs: java: [8, 8.0.192, 11.0.x, 11.0.3, 12, 13, 15, 16, 17 ] name: Java ${{ matrix.java }} env: - OS: ${{ matrix.os }} - JAVA: ${{ matrix.java-version }} + JAVA: ${{ matrix.java }} AWS_REGION: eu-west-1 steps: - uses: actions/checkout@v3 @@ -59,8 +58,14 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} + cache: 'maven' - name: Build with Maven run: mvn -Pbuild-without-spotbugs -B package --file pom.xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # 3.1.1 + if: ${{ matrix.java == '11.0.x' }} # 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 @@ -72,7 +77,7 @@ jobs: echo ${{ github.event.number }} echo ${{ github.event.number }} > ./pr/NR - uses: actions/upload-artifact@v2 - name: Updload artifact + name: Upload artifact with: name: pr path: pr/ diff --git a/README.md b/README.md index addb0f631..b22d0a948 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # AWS Lambda Powertools for Java -![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) +![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) ![Build status](https://github.com/awslabs/aws-lambda-powertools-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/awslabs/aws-lambda-powertools-java/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/awslabs/aws-lambda-powertools-java) + Powertools is a developer toolkit to implement Serverless best practices and increase developer velocity. diff --git a/examples/pom.xml b/examples/pom.xml index c61e858c5..d76d3780e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -32,4 +32,32 @@ <maven.deploy.skip>true</maven.deploy.skip> </properties> + <build> + <plugins> + <plugin> <!-- skip coverage for examples --> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <skip>true</skip> + </configuration> + </execution> + <execution> + <id>report</id> + <goals> + <goal>report</goal> + </goals> + <configuration> + <skip>true</skip> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </project> \ No newline at end of file diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index f7522303b..cc25c8011 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -6,12 +6,11 @@ <name>AWS Lambda Powertools for Java library Examples - Validation</name> <parent> - <artifactId>powertools-parent</artifactId> + <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> <version>1.14.0</version> </parent> - <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> diff --git a/pom.xml b/pom.xml index a4ae99072..259871a0b 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,6 @@ <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version> <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version> - <cobertura-maven-plugin.version>2.7</cobertura-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version> <maven-source-plugin.version>3.2.1</maven-source-plugin.version> @@ -321,11 +320,6 @@ <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco-maven-plugin.version}</version> </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>cobertura-maven-plugin</artifactId> - <version>${cobertura-maven-plugin.version}</version> - </plugin> <plugin> <groupId>org.sonatype.plugins</groupId> <artifactId>nexus-staging-maven-plugin</artifactId> @@ -409,17 +403,6 @@ </execution> </executions> </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>cobertura-maven-plugin</artifactId> - <configuration> - <formats> - <format>html</format> - <format>xml</format> - </formats> - <check /> - </configuration> - </plugin> <plugin> <groupId>org.sonatype.plugins</groupId> <artifactId>nexus-staging-maven-plugin</artifactId> From 5577f3a5f90f8696085545b0f5f0555de959bf82 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:58:03 +0100 Subject: [PATCH 0265/1008] Add link to powertools workshop (#1095) --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index d82a01e16..fa2d0fbf4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,7 +12,8 @@ Powertools is a suite of utilities for AWS Lambda Functions that makes tracing w !!! 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. + 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, + the [AWS Lambda Powertools for 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 From f8731bf37b9d428efef7885e746c30fc36f69c96 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <info@michelericciardi.co.uk> Date: Mon, 20 Mar 2023 11:37:21 +0100 Subject: [PATCH 0266/1008] docs(cloudformation-module): Improve Docs (#1090) --- docs/utilities/custom_resources.md | 174 ++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 30 deletions(-) diff --git a/docs/utilities/custom_resources.md b/docs/utilities/custom_resources.md index 1f2acf36e..d9f8494c3 100644 --- a/docs/utilities/custom_resources.md +++ b/docs/utilities/custom_resources.md @@ -3,17 +3,14 @@ title: Custom Resources description: Utility --- -[Custom resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) +[CloudFormation Custom resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) provide a way for [AWS Lambda functions]( https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html) to execute -provisioning logic whenever CloudFormation stacks are created, updated, or deleted. The CloudFormation utility enables -developers to write these Lambda functions in Java. +provisioning logic whenever CloudFormation stacks are created, updated, or deleted. -The utility provides a base `AbstractCustomResourceHandler` class which handles [custom resource request events]( -https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html), constructs -[custom resource responses](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html), and -sends them to the custom resources. Subclasses implement the provisioning logic and configure certain properties of -these response objects. +Powertools-cloudformation makes it easy to write Lambda functions in Java that are used as CloudFormation custom resources. +The utility reads incoming CloudFormation events, calls your custom code depending on the operation (CREATE, UPDATE or DELETE) and sends responses back to CloudFormation. +By using this library you do not need to write code to integrate with CloudFormation, and you only focus on writing the custom provisioning logic inside the Lambda function. ## Install @@ -40,57 +37,90 @@ To install this utility, add the following dependency to your project. ## Usage -Create a new `AbstractCustomResourceHandler` subclass and implement the `create`, `update`, and `delete` methods with -provisioning logic in the appropriate methods(s). +To utilise the feature, extend the `AbstractCustomResourceHandler` class in your Lambda handler class. +Next, implement and override the following 3 methods: `create`, `update` and `delete`. The `AbstractCustomResourceHandler` invokes the right method according to the CloudFormation [custom resource request event]( +https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html) it receives. +Inside the methods, implement your custom provisioning logic, and return a `Response`. The `AbstractCustomResourceHandler` takes your `Response`, builds a +[custom resource responses](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html) and sends it to CloudFormation automatically. -As an example, if a Lambda function only needs to provision something when a stack is created, put the provisioning -logic exclusively within the `create` method; the other methods can just return `null`. +Custom resources notify cloudformation either of `SUCCESS` or `FAILED` status. You have 2 utility methods to represent these responses: `Response.success(physicalResourceId)` and `Response.failed(physicalResourceId)`. +The `physicalResourceId` is an identifier that is used during the lifecycle operations of the Custom Resource. +You should generate a `physicalResourceId` during the `CREATE` operation, CloudFormation stores the `physicalResourceId` and includes it in `UPDATE` and `DELETE` events. -```java hl_lines="8 9 10 11" +Here an example of how to implement a Custom Resource using the powertools-cloudformation library: + +```java hl_lines="10-16 21-27 32-38" 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 ProvisionOnCreateHandler extends AbstractCustomResourceHandler { +public class MyCustomResourceHandler extends AbstractCustomResourceHandler { @Override protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) { - doProvisioning(); - return Response.success(); + String physicalResourceId = "sample-resource-id-" + UUID.randomUUID(); //Create a unique ID for your resource + ProvisioningResult provisioningResult = doProvisioning(physicalResourceId); + if(provisioningResult.isSuccessful()){ //check if the provisioning was successful + return Response.success(physicalResourceId); + }else{ + return Response.failed(physicalResourceId); + } } @Override protected Response update(CloudFormationCustomResourceEvent updateEvent, Context context) { - return null; + String physicalResourceId = updateEvent.getPhysicalResourceId(); //Get the PhysicalResourceId from CloudFormation + UpdateResult updateResult = doUpdates(physicalResourceId); + if(updateResult.isSuccessful()){ //check if the update operations were successful + return Response.success(physicalResourceId); + }else{ + return Response.failed(physicalResourceId); + } } @Override protected Response delete(CloudFormationCustomResourceEvent deleteEvent, Context context) { - return null; + String physicalResourceId = deleteEvent.getPhysicalResourceId(); //Get the PhysicalResourceId from CloudFormation + DeleteResult deleteResult = doDeletes(physicalResourceId); + if(deleteResult.isSuccessful()){ //check if the delete operations were successful + return Response.success(physicalResourceId); + }else{ + return Response.failed(physicalResourceId); + } } } ``` -### Signaling Provisioning Failures +### Missing `Response` and exception handling -If provisioning fails, the stack creation/modification/deletion as a whole can be failed by either throwing a -`RuntimeException` or by explicitly returning a `Response` with a failed status, e.g. `Response.failure()`. +If a `Response` is not returned by your code, `AbstractCustomResourceHandler` defaults the response to `SUCCESS`. +If your code raises an exception (which is not handled), the `AbstractCustomResourceHandler` defaults the response to `FAILED`. -### Configuring Response Objects +In both of the scenarios, powertools-java will return the `physicalResourceId` to CloudFormation based on the following logic: +- For CREATE operations, the `LogStreamName` from the Lambda context is used. +- For UPDATE and DELETE operations, the `physicalResourceId` provided in the `CloudFormationCustomResourceEvent` is used. -When provisioning results in data to be shared with other parts of the stack, include this data within the returned -`Response` instance. +#### Why do you need a physicalResourceId? -This Lambda function creates a [Chime AppInstance](https://docs.aws.amazon.com/chime/latest/dg/create-app-instance.html) +It is recommended that you always explicitly provide a `physicalResourceId` in your response rather than letting powertools generate if for you because `physicalResourceId` has a crucial role in the lifecycle of a CloudFormation custom resource. +If the `physicalResourceId` changes between calls from Cloudformation, for instance in response to an `Update` event, Cloudformation [treats the resource update as a replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html). + +### Customising a response + +As well as the `Response.success(physicalResourceId)` and `Response.failed(physicalResourceId)`, you can customise the `Response` by using the `Response.builder()`. +You customise the responses when you need additional attributes to be shared with other parts of the CloudFormation stack. + +In the example below, the Lambda function creates a [Chime AppInstance](https://docs.aws.amazon.com/chime/latest/dg/create-app-instance.html) and maps the returned ARN to a "ChimeAppInstanceArn" attribute. -```java hl_lines="11 12 13 14" +```java hl_lines="12-17" public class ChimeAppInstanceHandler extends AbstractCustomResourceHandler { @Override protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) { + String physicalResourceId = "my-app-name-" + UUID.randomUUID(); //Create a unique ID CreateAppInstanceRequest chimeRequest = CreateAppInstanceRequest.builder() - .name("my-app-name") + .name(physicalResourceId) .build(); CreateAppInstanceResponse chimeResponse = ChimeClient.builder() .region("us-east-1") @@ -99,6 +129,8 @@ public class ChimeAppInstanceHandler extends AbstractCustomResourceHandler { Map<String, String> chimeAtts = Map.of("ChimeAppInstanceArn", chimeResponse.appInstanceArn()); return Response.builder() .value(chimeAtts) + .status(Response.Status.SUCCESS) + .physicalResourceId(physicalResourceId) .build(); } } @@ -113,6 +145,7 @@ For the example above the following response payload will be sent. "StackId": "arn:aws:cloudformation:us-east-1:123456789000:stack/Custom-stack/59e4d2d0-2fe2-10ec-b00e-124d7c1c5f15", "RequestId": "7cae0346-0359-4dff-b80a-a82f247467b6", "LogicalResourceId:": "ChimeTriggerResource", + "PhysicalResourceId:": "my-app-name-db4a47b9-0cac-45ba-8cc4-a480490c5779", "NoEcho": false, "Data": { "ChimeAppInstanceArn": "arn:aws:chime:us-east-1:123456789000:app-instance/150972c2-5490-49a9-8ba7-e7da4257c16a" @@ -120,7 +153,7 @@ For the example above the following response payload will be sent. } ``` -Once the custom resource receives this response, it's "ChimeAppInstanceArn" attribute is set and the +Once the custom resource receives this response, its "ChimeAppInstanceArn" attribute is set and the [Fn::GetAtt function]( https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html) may be used to retrieve the attribute value and make it available to other resources in the stack. @@ -130,11 +163,14 @@ retrieve the attribute value and make it available to other resources in the sta If any attributes are sensitive, enable the "noEcho" flag to mask the output of the custom resource when it's retrieved with the Fn::GetAtt function. -```java hl_lines="6" +```java hl_lines="9" public class SensitiveDataHandler extends AbstractResourceHandler { @Override protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) { + String physicalResourceId = "my-sensitive-resource-" + UUID.randomUUID(); //Create a unique ID return Response.builder() + .status(Response.Status.SUCCESS) + .physicalResourceId(physicalResourceId) .value(Map.of("SomeSecret", sensitiveValue)) .noEcho(true) .build(); @@ -148,7 +184,7 @@ Although using a `Map` as the Response's value is the most straightforward way t any arbitrary `java.lang.Object` may be used. By default, these objects are serialized with an internal Jackson `ObjectMapper`. If the object requires special serialization logic, a custom `ObjectMapper` can be specified. -```java hl_lines="21 22 23 24" +```java hl_lines="14-16 26" public class CustomSerializationHandler extends AbstractResourceHandler { /** * Type representing the custom response Data. @@ -168,11 +204,89 @@ public class CustomSerializationHandler extends AbstractResourceHandler { @Override protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) { + String physicalResourceId = "my-policy-name-" + UUID.randomUUID(); //Create a unique ID Policy policy = new Policy(); return Response.builder() + .status(Response.Status.SUCCESS) + .physicalResourceId(physicalResourceId) .value(policy) .objectMapper(policyMapper) // customize serialization .build(); } } ``` + +## Advanced + +### Understanding the CloudFormation custom resource lifecycle + +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` +```mermaid +stateDiagram + direction LR + createState: Create custom resource + [*] --> createState + createState --> CREATE_COMPLETE + createState --> CREATE_FAILED +``` + +If the resource is created successfully, the `physicalResourceId` is stored by CloudFormation for future operations. +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. +During the update, the custom resource may update successfully, or may fail the update. +```mermaid +stateDiagram + direction LR + updateState: Update custom resource + [*] --> updateState + updateState --> UPDATE_COMPLETE + updateState --> UPDATE_FAILED +``` + +In both of these scenarios, the custom resource can return the same `physicalResourceId` it received in the CloudFormation event, or a different `physicalResourceId`. +Semantically an `UPDATE_COMPLETE` that returns the same `physicalResourceId` it received indicates that the existing resource was updated successfully. +Instead, an `UPDATE_COMPLETE` with a different `physicalResourceId` means that a new physical resource was created successfully. +```mermaid +flowchart BT + id1(Logical resource) + id2(Previous physical Resource) + id3(New physical Resource) + id2 --> id1 + id3 --> id1 +``` +Therefore, after the custom resource update completed or failed, there may be other cleanup operations by Cloudformation during the rollback, as described in the diagram below: +```mermaid +stateDiagram + state if_state <<choice>> + updateState: Update custom resource + deletePrev: DELETE resource with previous physicalResourceId + updatePrev: Rollback - UPDATE resource with previous properties + noOp: No further operations + [*] --> updateState + updateState --> UPDATE_COMPLETE + UPDATE_COMPLETE --> if_state + if_state --> noOp : Same physicalResourceId + if_state --> deletePrev : Different physicalResourceId + updateState --> UPDATE_FAILED + UPDATE_FAILED --> updatePrev +``` + +#### Deleting a custom resource + +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`. + +```mermaid +stateDiagram + direction LR + deleteState: Delete custom resource + [*] --> deleteState + deleteState --> DELETE_COMPLETE + deleteState --> DELETE_FAILED +``` \ No newline at end of file From 365587d3e51ad10787a9148ecbbb7bce0e72b648 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:46:54 +0100 Subject: [PATCH 0267/1008] feat: Add DynamoDB provider to parameters module (#1091) --- docs/utilities/parameters.md | 66 +++++- powertools-parameters/pom.xml | 4 + .../parameters/DynamoDbProvider.java | 191 ++++++++++++++++++ .../powertools/parameters/ParamManager.java | 34 +++- .../DynamoDbProviderSchemaException.java | 11 + .../parameters/DynamoDbProviderE2ETest.java | 99 +++++++++ .../parameters/DynamoDbProviderTest.java | 168 +++++++++++++++ .../ParamManagerIntegrationTest.java | 16 ++ 8 files changed, 578 insertions(+), 11 deletions(-) create mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java create mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java create mode 100644 powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java create mode 100644 powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index e2d0cb965..e8df1f8d6 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -5,8 +5,9 @@ description: Utility The parameters utility provides 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) or -[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/). It also provides a base class to create your parameter provider implementation. +[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. **Key features** @@ -40,11 +41,12 @@ To install this utility, add the following dependency to your project. 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` +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` ## SSM Parameter Store @@ -74,7 +76,7 @@ in order to get data from other regions or use specific credentials. } ``` -=== "SSMProvider with an explicit region" +=== "SSMProvider with a custom client" ```java hl_lines="5 7" import software.amazon.lambda.powertools.parameters.SSMProvider; @@ -149,7 +151,7 @@ in order to get data from other regions or use specific credentials. } ``` -=== "SecretsProvider with an explicit region" +=== "SecretsProvider with a custom client" ```java hl_lines="5 7" import software.amazon.lambda.powertools.parameters.SecretsProvider; @@ -166,6 +168,52 @@ in order to get data from other regions or use specific credentials. } ``` +## 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. + +=== "DynamoDbProvider" + + ```java hl_lines="6 9" + import software.amazon.lambda.powertools.parameters.DynamoDbProvider; + import software.amazon.lambda.powertools.parameters.ParamManager; + + public class AppWithDynamoDbParameters implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + // Get an instance of the DynamoDbProvider + DynamoDbProvider ddbProvider = ParamManager.getDynamoDbProvider("my-parameters-table"); + + // Retrieve a single parameter + String value = ddbProvider.get("my-key"); + } + ``` + +=== "DynamoDbProvider with a custom client" + + ```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"); + } + ``` + + + ## Advanced configuration ### Caching diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 66eedce1e..befb57d09 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -73,6 +73,10 @@ <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>dynamodb</artifactId> + </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> 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 new file mode 100644 index 000000000..08031e185 --- /dev/null +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java @@ -0,0 +1,191 @@ +package software.amazon.lambda.powertools.parameters; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +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.*; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table + * is described in the Powertools documentation. + * + * @see <a href="https://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters">Parameters provider documentation</a> + * + */ +public class DynamoDbProvider extends BaseProvider { + + private final DynamoDbClient client; + private final String tableName; + + public DynamoDbProvider(CacheManager cacheManager, String tableName) { + this(cacheManager, DynamoDbClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(), + tableName + ); + + } + + DynamoDbProvider(CacheManager cacheManager, DynamoDbClient client, String tableName) { + super(cacheManager); + this.client = client; + this.tableName = tableName; + } + + /** + * 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())); + + + } + + /** + * 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(); + } + + static class Builder { + private DynamoDbClient client; + private String table; + private CacheManager cacheManager; + private TransformationManager transformationManager; + + /** + * 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) { + provider = new DynamoDbProvider(cacheManager, client, table); + } else { + provider = new DynamoDbProvider(cacheManager, 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 index 0131ae179..e4c70acb0 100644 --- 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 @@ -15,6 +15,7 @@ import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.ssm.SsmClient; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @@ -36,8 +37,8 @@ public final class ParamManager { /** * Get a concrete implementation of {@link BaseProvider}.<br/> - * You can specify {@link SecretsProvider} or {@link SSMProvider} or create your custom provider - * by extending {@link BaseProvider} if you need to integrate with a different parameter store. + * You can specify {@link SecretsProvider}, {@link SSMProvider}, {@link DynamoDbProvider}, or create your + * custom provider by extending {@link BaseProvider} if you need to integrate with a different parameter store. * @return a {@link SecretsProvider} */ public static <T extends BaseProvider> T getProvider(Class<T> providerClass) { @@ -65,6 +66,21 @@ 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 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. @@ -91,6 +107,20 @@ public static SSMProvider getSsmProvider(SsmClient client) { .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()); + } + public static CacheManager getCacheManager() { return cacheManager; } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java new file mode 100644 index 000000000..b7574e81d --- /dev/null +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java @@ -0,0 +1,11 @@ +package software.amazon.lambda.powertools.parameters.exception; + +/** + * Thrown when the DynamoDbProvider comes across parameter data that + * does not meet the DynamoDB parameters schema. + */ +public class DynamoDbProviderSchemaException extends RuntimeException { + public DynamoDbProviderSchemaException(String msg) { + super(msg); + } +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java new file mode 100644 index 000000000..c9397676b --- /dev/null +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java @@ -0,0 +1,99 @@ +package software.amazon.lambda.powertools.parameters; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +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.parameters.cache.CacheManager; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This class provides simple end-to-end style testing of the DynamoDBProvider class. + * It is ignored, for now, as it requires AWS access and that's not yet run as part + * of our unit test suite in the cloud. + * + * The test is kept here for 1/ local development and 2/ in preparation for future + * E2E tests running in the cloud CI. Once the E2E test structure is merged we + * will move this across. + */ +@Disabled +public class DynamoDbProviderE2ETest { + + final String ParamsTestTable = "ddb-params-test"; + final String MultiparamsTestTable = "ddb-multiparams-test"; + private final DynamoDbClient ddbClient; + + public DynamoDbProviderE2ETest() { + // Create a DDB client to inject test data into our test tables + ddbClient = DynamoDbClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .build(); + + + } + + @Test + public void TestGetValue() { + + // Arrange + HashMap<String, AttributeValue> testItem = new HashMap<String, AttributeValue>(); + testItem.put("id", AttributeValue.fromS("test_param")); + testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); + ddbClient.putItem(PutItemRequest.builder() + .tableName(ParamsTestTable) + .item(testItem) + .build()); + + // Act + DynamoDbProvider provider = makeProvider(ParamsTestTable); + String value = provider.getValue("test_param"); + + // Assert + assertThat(value).isEqualTo("the_value_is_hello!"); + } + + @Test + public void TestGetValues() { + + // Arrange + HashMap<String, AttributeValue> testItem = new HashMap<String, AttributeValue>(); + 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) + .item(testItem) + .build()); + + HashMap<String, AttributeValue> testItem2 = new HashMap<String, AttributeValue>(); + 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) + .item(testItem2) + .build()); + + // Act + DynamoDbProvider provider = makeProvider(MultiparamsTestTable); + Map<String, String> values = provider.getMultipleValues("test_param"); + + // Assert + assertThat(values.size()).isEqualTo(2); + assertThat(values.get("test_param_part_1")).isEqualTo("the_value_is_hello!"); + assertThat(values.get("test_param_part_2")).isEqualTo("the_value_is_still_hello!"); + } + + private DynamoDbProvider makeProvider(String tableName) { + return new DynamoDbProvider(new CacheManager(), 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/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java new file mode 100644 index 000000000..0e5b734d6 --- /dev/null +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java @@ -0,0 +1,168 @@ +package software.amazon.lambda.powertools.parameters; + +import org.junit.jupiter.api.Assertions; +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 software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.*; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.MockitoAnnotations.openMocks; + +public class DynamoDbProviderTest { + + @Mock + DynamoDbClient client; + + @Captor + ArgumentCaptor<GetItemRequest> getItemValueCaptor; + + @Captor + ArgumentCaptor<QueryRequest> queryRequestCaptor; + + + private DynamoDbProvider provider; + private final String tableName = "ddb-test-table"; + + @BeforeEach + public void init() { + openMocks(this); + CacheManager cacheManager = new CacheManager(); + provider = new DynamoDbProvider(cacheManager, client, tableName); + } + + + @Test + public void getValue() { + + // Arrange + String key = "Key1"; + String expectedValue = "Value1"; + HashMap<String, AttributeValue> responseData = new HashMap<String, AttributeValue>(); + responseData.put("id", AttributeValue.fromS(key)); + responseData.put("value", AttributeValue.fromS(expectedValue)); + GetItemResponse response = GetItemResponse.builder() + .item(responseData) + .build(); + Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(response); + + // Act + String value = provider.getValue(key); + + // Assert + assertThat(value).isEqualTo(expectedValue); + assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(getItemValueCaptor.getValue().key().get("id").s()).isEqualTo(key); + } + + + @Test + public void getValueWithoutResultsReturnsNull() { + // Arrange + Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() + .item(null) + .build()); + + // Act + String value = provider.getValue("key"); + + // Assert + assertThat(value).isEqualTo(null); + } + + @Test + public void getValueWithMalformedRowThrows() { + // Arrange + String key = "Key1"; + HashMap<String, AttributeValue> responseData = new HashMap<String, AttributeValue>(); + 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, () -> { + String value = provider.getValue(key); + }); + } + + + @Test + public void getValues() { + + // Arrange + String key = "Key1"; + String subkey1 = "Subkey1"; + String val1 = "Val1"; + String subkey2 = "Subkey2"; + String val2 = "Val2"; + HashMap<String, AttributeValue> item1 = new HashMap<String, AttributeValue>(); + 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>(); + item2.put("id", AttributeValue.fromS(key)); + item2.put("sk", AttributeValue.fromS(subkey2)); + item2.put("value", AttributeValue.fromS(val2)); + QueryResponse response = QueryResponse.builder() + .items(item1, item2) + .build(); + Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); + + // Act + Map<String, String> values = provider.getMultipleValues(key); + + // Assert + 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().keyConditionExpression()).isEqualTo("id = :v_id"); + assertThat(queryRequestCaptor.getValue().expressionAttributeValues().get(":v_id").s()).isEqualTo(key); + } + + @Test + public void getValuesWithoutResultsReturnsNull() { + // Arrange + Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn( + QueryResponse.builder().items().build()); + + // Act + Map<String, String> values = provider.getMultipleValues(UUID.randomUUID().toString()); + + // Assert + assertThat(values.size()).isEqualTo(0); + } + + @Test + public void getValuesWithMalformedRowThrows() { + // Arrange + String key = "Key1"; + HashMap<String, AttributeValue> item1 = new HashMap<String, AttributeValue>(); + item1.put("id", AttributeValue.fromS(key)); + item1.put("sk", AttributeValue.fromS("some-subkey")); + item1.put("not-value", AttributeValue.fromS("somevalue")); + QueryResponse response = QueryResponse.builder() + .items(item1) + .build(); + Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); + + // Assert + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { + // Act + Map<String, String> values = provider.getMultipleValues(key); + }); + } + + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java index 0b4a2093f..ec1672ead 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java @@ -19,6 +19,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; @@ -41,6 +42,9 @@ public class ParamManagerIntegrationTest { @Mock SsmClient ssmClient; + @Mock + DynamoDbClient ddbClient; + @Captor ArgumentCaptor<GetParameterRequest> ssmParamCaptor; @@ -116,4 +120,16 @@ public void secretsProvider_get() { assertThat(secretsProvider.get("keys")).isEqualTo(expectedValue); // second time is from cache verify(secretsManagerClient, times(1)).getSecretValue(any(GetSecretValueRequest.class)); } + + @Test + public void getDynamoDbProvider() { + + // Act + DynamoDbProvider provider = ParamManager.getDynamoDbProvider(ddbClient, "test-table"); + + // Assert + assertThat(provider).isNotNull(); + + + } } From 7e68ac13b22988588936c52b18f5e9b7d2f0cad2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:20:39 +0100 Subject: [PATCH 0268/1008] build(deps): bump json-schema-validator from 1.0.77 to 1.0.78 (#1100) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.77 to 1.0.78. --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 14c4e4f9e..21e0eff88 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.77</version> + <version>1.0.78</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From a1539b23ebfc0e52e9c8b61c7ba1689970438142 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:21:52 +0100 Subject: [PATCH 0269/1008] build(deps): bump aws-lambda-java-events from 3.11.0 to 3.11.1 (#1099) Bumps [aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs) from 3.11.0 to 3.11.1. --- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 640cab182..b2dbde679 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.0</version> + <version>3.11.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index ee151ce0f..9d23e9635 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.0</version> + <version>3.11.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index f3797c948..6e8f862de 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -30,7 +30,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.0</version> + <version>3.11.1</version> </dependency> <!-- Test dependencies --> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index f1d45f760..156254d66 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -30,7 +30,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.0</version> + <version>3.11.1</version> </dependency> <!-- Test dependencies --> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 8abcfa56f..d158f7173 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.0</version> + <version>3.11.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/pom.xml b/pom.xml index 259871a0b..1e9fda2a9 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ <payloadoffloading-common.version>2.1.2</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.0</lambda.events.version> + <lambda.events.version>3.11.1</lambda.events.version> <lambda.serial.version>1.0.0</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version> From 23c7d4c9147c18979845081988a52e501c01c9ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:22:39 +0100 Subject: [PATCH 0270/1008] build(deps): bump aws.sdk.version from 2.20.11 to 2.20.27 (#1098) Bumps `aws.sdk.version` from 2.20.11 to 2.20.27. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index d158f7173..6dfd66941 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.11</version> + <version>2.20.27</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 1e9fda2a9..099ad45ab 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.11</aws.sdk.version> + <aws.sdk.version>2.20.27</aws.sdk.version> <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 0fdc34b65ffcc65250d609dcaf768644d6d6e080 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 15:01:47 +0100 Subject: [PATCH 0271/1008] build(deps): bump aws-lambda-java-serialization from 1.0.0 to 1.1.2 (#1088) Bumps [aws-lambda-java-serialization](https://github.com/aws/aws-lambda-java-libs) from 1.0.0 to 1.1.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 099ad45ab..d77f5cde7 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.2</lambda.core.version> <lambda.events.version>3.11.1</lambda.events.version> - <lambda.serial.version>1.0.0</lambda.serial.version> + <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version> <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version> From 482778be751781e6375b4302861102b2b2459e17 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <mriccia@amazon.com> Date: Tue, 21 Mar 2023 10:14:05 +0100 Subject: [PATCH 0272/1008] chore: update the project version to 1.15.0 (#1097) --- CHANGELOG.md | 21 +++++++++++++++++++ README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-core/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- .../powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- .../powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 22 files changed, 44 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57a1f5d6e..302f28b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,27 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.15.0] - 2023-03-20 + +### 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://awslabs.github.io/aws-lambda-powertools-java/utilities/custom_resources/). (#1082) by @msailes +* Update how a Lambda request handler method is identified (#1058) by @humanzz + +### 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 + +### Documentation + +* Improve `powertools-cloudformation` docs (#1090) by @mriccia +* Add link to Lambda powertools workshop (#1095) by @scottgerring +* Fix mdocs and git revision plugin integration (#1066) by @machafer + + ## [1.14.0] - 2023-02-17 ### Added diff --git a/README.md b/README.md index b22d0a948..835ddf7f5 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools is available in Maven Central. You can use your favourite dependency <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.14.0</version> + <version>1.15.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.14.0</version> + <version>1.15.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.14.0</version> + <version>1.15.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index d76d3780e..546685119 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Examples</name> diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index b2dbde679..73d874839 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -8,7 +8,7 @@ <parent> <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <dependencies> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 9d23e9635..64ef72ac3 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -8,7 +8,7 @@ <parent> <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <dependencies> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 6e8f862de..aca45508a 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -8,7 +8,7 @@ <parent> <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <dependencies> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 156254d66..a02c32820 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -8,7 +8,7 @@ <parent> <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 6dfd66941..132bd7f38 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -8,7 +8,7 @@ <parent> <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <dependencies> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index cc25c8011..13c2ff816 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -8,7 +8,7 @@ <parent> <artifactId>powertools-examples</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <dependencies> diff --git a/mkdocs.yml b/mkdocs.yml index 10b07b21d..430c0bfcc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,7 +82,7 @@ extra_javascript: extra: powertools: - version: 1.14.0 + version: 1.15.0 repo_url: https://github.com/awslabs/aws-lambda-powertools-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index d77f5cde7..462a71778 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.14.0</version> + <version>1.15.0</version> <packaging>pom</packaging> <name>AWS Lambda Powertools for Java library Parent</name> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 5366cdc26..727c96e70 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index fea692bfd..2efb5ff92 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Core</name> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 020108fd5..4fc2ff4a1 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 58f239263..665030065 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 3f409e949..4d6ca0eb2 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index befb57d09..3232d01cf 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 06cb1b133..592cd5610 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 63aa72606..fb5d6c276 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 153605bc9..d3d4bde62 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 3ca97bebc..8b8dd51cb 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 21e0eff88..c60d38c68 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <name>AWS Lambda Powertools for Java validation library</name> From 15616321bff43a407026a6a0da575257d8ebfd57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 21 Mar 2023 12:52:37 +0100 Subject: [PATCH 0273/1008] feat: end-to-end tests for core modules and idempotency (#970) * add e2e tests for logging * add e2e tests for metrics * add e2e tests for idempotency * add e2e tests for tracing --- pom.xml | 3 +- powertools-e2e-tests/README.md | 16 + .../handlers/idempotency/pom.xml | 57 +++ .../lambda/powertools/e2e/Function.java | 48 +++ .../amazon/lambda/powertools/e2e/Input.java | 20 + powertools-e2e-tests/handlers/logging/pom.xml | 57 +++ .../lambda/powertools/e2e/Function.java | 22 ++ .../amazon/lambda/powertools/e2e/Input.java | 27 ++ .../logging/src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/metrics/pom.xml | 57 +++ .../lambda/powertools/e2e/Function.java | 26 ++ .../amazon/lambda/powertools/e2e/Input.java | 29 ++ powertools-e2e-tests/handlers/pom.xml | 119 ++++++ powertools-e2e-tests/handlers/tracing/pom.xml | 57 +++ .../lambda/powertools/e2e/Function.java | 40 ++ .../amazon/lambda/powertools/e2e/Input.java | 17 + .../tracing/src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/pom.xml | 175 +++++++++ .../lambda/powertools/IdempotencyE2ET.java | 61 +++ .../amazon/lambda/powertools/LoggingE2ET.java | 77 ++++ .../amazon/lambda/powertools/MetricsE2ET.java | 73 ++++ .../amazon/lambda/powertools/TracingE2ET.java | 88 +++++ .../powertools/testutils/Infrastructure.java | 355 ++++++++++++++++++ .../powertools/testutils/JavaRuntime.java | 37 ++ .../testutils/lambda/InvocationResult.java | 43 +++ .../testutils/lambda/LambdaInvoker.java | 39 ++ .../testutils/logging/InvocationLogs.java | 71 ++++ .../testutils/metrics/MetricsFetcher.java | 95 +++++ .../testutils/tracing/SegmentDocument.java | 189 ++++++++++ .../powertools/testutils/tracing/Trace.java | 21 ++ .../testutils/tracing/TraceFetcher.java | 210 +++++++++++ .../src/test/resources/logback-test.xml | 20 + 32 files changed, 2180 insertions(+), 1 deletion(-) create mode 100644 powertools-e2e-tests/README.md create mode 100644 powertools-e2e-tests/handlers/idempotency/pom.xml create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/logging/pom.xml create mode 100644 powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/metrics/pom.xml create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/pom.xml create mode 100644 powertools-e2e-tests/handlers/tracing/pom.xml create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/pom.xml create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java create mode 100644 powertools-e2e-tests/src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index 462a71778..8f2701054 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,8 @@ <module>powertools-validation</module> <module>powertools-test-suite</module> <module>powertools-cloudformation</module> - <module>powertools-idempotency</module> + <module>powertools-idempotency</module> + <module>powertools-e2e-tests</module> <module>examples</module> </modules> diff --git a/powertools-e2e-tests/README.md b/powertools-e2e-tests/README.md new file mode 100644 index 000000000..42fe9191b --- /dev/null +++ b/powertools-e2e-tests/README.md @@ -0,0 +1,16 @@ +## End-to-end tests +This module is internal and meant to be used for end-to-end (E2E) testing of Lambda Powertools for Java. + +__Prerequisites__: +- An AWS account is needed as well as a local environment able to reach this account +([credentials](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials.html)). +- [Java 11+](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html) +- [Docker](https://docs.docker.com/engine/install/) + +To execute the E2E tests, use the following command: `export JAVA_VERSION=11 && mvn clean verify -Pe2e` + +### Under the hood +This module leverages the following components: +- AWS CDK to define the infrastructure and synthesize a CloudFormation template and the assets (lambda function packages) +- The AWS S3 SDK to push the assets on S3 +- The AWS CloudFormation SDK to deploy the template \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml new file mode 100644 index 000000000..aa7870389 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -0,0 +1,57 @@ +<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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-idempotency</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using powertools idempotency</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</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-idempotency</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/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 new file mode 100644 index 000000000..cc6eec4fa --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,48 @@ +package software.amazon.lambda.powertools.e2e; + +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.Idempotent; +import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; + +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.TimeZone; + + +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(); + } + + @Idempotent + 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/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..c5c2a121e --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,20 @@ +package software.amazon.lambda.powertools.e2e; + +public class Input { + private String message; + + public Input(String message) { + this.message = message; + } + + public Input() { + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml new file mode 100644 index 000000000..a46702ca4 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -0,0 +1,57 @@ +<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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-logging</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using powertools logging</name> + + <dependencies> + <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.codehaus.mojo</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> +</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/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..5a9a87109 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,22 @@ +package software.amazon.lambda.powertools.e2e; + +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; + +public class Function implements RequestHandler<Input, String> { + + private static final Logger LOG = LogManager.getLogger(Function.class); + + @Logging + public String handleRequest(Input input, Context context) { + + LoggingUtils.appendKeys(input.getKeys()); + LOG.info(input.getMessage()); + + return "OK"; + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..83afbbd5a --- /dev/null +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,27 @@ +package software.amazon.lambda.powertools.e2e; + +import java.util.Map; + +public class Input { + private String message; + private Map<String, String> keys; + + public Input() { + } + + 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/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/logging/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/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml new file mode 100644 index 000000000..e591f4966 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -0,0 +1,57 @@ +<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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-metrics</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using powertools metrics</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</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-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> + </plugin> + </plugins> + </build> +</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 new file mode 100644 index 000000000..e643de9d5 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,26 @@ +package software.amazon.lambda.powertools.e2e; + +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; +import software.amazon.lambda.powertools.metrics.MetricsUtils; + +public class Function implements RequestHandler<Input, String> { + + MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); + + @Metrics(captureColdStart = true) + public String handleRequest(Input input, Context context) { + + DimensionSet dimensionSet = new DimensionSet(); + input.getDimensions().forEach((key, value) -> dimensionSet.addDimension(key, value)); + metricsLogger.putDimensions(dimensionSet); + + input.getMetrics().forEach((key, value) -> metricsLogger.putMetric(key, value, Unit.COUNT)); + + 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 new file mode 100644 index 000000000..5ff8a7125 --- /dev/null +++ b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,29 @@ +package software.amazon.lambda.powertools.e2e; + +import java.util.Map; + +public class Input { + private Map<String, Double> metrics; + + private Map<String, String> dimensions; + + public Map<String, Double> getMetrics() { + return metrics; + } + + public void setMetrics(Map<String, Double> metrics) { + this.metrics = metrics; + } + + public Input() { + } + + + public Map<String, String> getDimensions() { + return dimensions; + } + + public void setDimensions(Map<String, String> dimensions) { + this.dimensions = dimensions; + } +} diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml new file mode 100644 index 000000000..b82a9a0c9 --- /dev/null +++ b/powertools-e2e-tests/handlers/pom.xml @@ -0,0 +1,119 @@ +<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</groupId> + <artifactId>e2e-test-handlers-parent</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + <name>Handlers for End-to-End tests</name> + <description>Fake handlers that use Lambda Powertools for Java.</description> + + <properties> + <lambda.powertools.version>1.14.0</lambda.powertools.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + + <lambda.java.core>1.2.2</lambda.java.core> + <lambda.java.events>3.11.0</lambda.java.events> + <maven.shade.version>3.2.4</maven.shade.version> + <aspectj.version>1.14.0</aspectj.version> + <maven.compiler.version>3.10.1</maven.compiler.version> + </properties> + + <modules> + <module>logging</module> + <module>tracing</module> + <module>metrics</module> + <module>idempotency</module> + </modules> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>${lambda.java.core}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>${lambda.java.events}</version> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven.shade.version}</version> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <finalName>function</finalName> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <transformers> + <transformer + implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>io.github.edwgiz</groupId> + <artifactId>log4j-maven-shade-plugin-extensions</artifactId> + <version>2.17.2</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>${aspectj.version}</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven.compiler.version}</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + +</project> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml new file mode 100644 index 000000000..831669a3d --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -0,0 +1,57 @@ +<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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-tracing</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using powertools tracing</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</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-tracing</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/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 new file mode 100644 index 000000000..f7b2c5e5d --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,40 @@ +package software.amazon.lambda.powertools.e2e; + +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.tracing.TracingUtils; + +public class Function implements RequestHandler<Input, String> { + + @Tracing + public String handleRequest(Input input, Context context) { + try { + Thread.sleep(100); // simulate stuff + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + String message = buildMessage(input.getMessage(), context.getFunctionName()); + + TracingUtils.withSubsegment("internal_stuff", subsegment -> { + try { + Thread.sleep(100); // simulate stuff + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + return message; + } + + @Tracing + private String buildMessage(String message, String funcName) { + TracingUtils.putAnnotation("message", message); + try { + Thread.sleep(150); // simulate other stuff + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + 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 new file mode 100644 index 000000000..29cf618ba --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,17 @@ +package software.amazon.lambda.powertools.e2e; + +public class Input { + private String message; + + public Input() { + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/tracing/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/tracing/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/pom.xml b/powertools-e2e-tests/pom.xml new file mode 100644 index 000000000..77e9fa458 --- /dev/null +++ b/powertools-e2e-tests/pom.xml @@ -0,0 +1,175 @@ +<?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>1.14.0</version> + </parent> + + <artifactId>powertools-e2e-tests</artifactId> + <name>AWS Lambda Powertools for Java library End-to-end tests</name> + <description>AWS Lambda Powertools for Java End-To-End Tests</description> + + <properties> + <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> + <maven.compiler.source>8</maven.compiler.source> + <maven.compiler.target>8</maven.compiler.target> + <constructs.version>10.1.138</constructs.version> + <cdk.version>2.47.0</cdk.version> + </properties> + + <dependencies> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.4.4</version> + </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>cloudwatch</artifactId> + <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>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>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</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> + <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> + <version>${constructs.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <version>${aws.sdk.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>cloudformation</artifactId> + <version>${aws.sdk.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sts</artifactId> + <version>${aws.sdk.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + <version>1.33</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.10.1</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>e2e</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <version>2.22.2</version> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + <configuration> + <includes> + <include>**/*E2ET.java</include> + </includes> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project> \ No newline at end of file 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 new file mode 100644 index 000000000..4133f986f --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java @@ -0,0 +1,61 @@ +package software.amazon.lambda.powertools; + +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.Timeout; +import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; + +import java.time.Year; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +public class IdempotencyE2ET { + private static Infrastructure infrastructure; + private static String functionName; + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + public static void setup() { + infrastructure = Infrastructure.builder() + .testName(IdempotencyE2ET.class.getSimpleName()) + .pathToFunction("idempotency") + .idempotencyTable("idempo") + .environmentVariables(Collections.singletonMap("IDEMPOTENCY_TABLE", "idempo")) + .build(); + functionName = infrastructure.deploy(); + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) + infrastructure.destroy(); + } + + @Test + public void test_ttlNotExpired_sameResult_ttlExpired_differentResult() throws InterruptedException { + // GIVEN + String event = "{\"message\":\"TTL 10sec\"}"; + + // WHEN + // First invocation + InvocationResult result1 = invokeFunction(functionName, event); + + // Second invocation (should get same result) + InvocationResult result2 = invokeFunction(functionName, event); + + Thread.sleep(12000); + + // Third invocation (should get different result) + InvocationResult result3 = invokeFunction(functionName, event); + + // THEN + Assertions.assertThat(result1.getResult()).contains(Year.now().toString()); + Assertions.assertThat(result2.getResult()).isEqualTo(result1.getResult()); + Assertions.assertThat(result3.getResult()).isNotEqualTo(result2.getResult()); + } +} 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 new file mode 100644 index 000000000..15c5eb935 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java @@ -0,0 +1,77 @@ +package software.amazon.lambda.powertools; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +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 java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +import static software.amazon.lambda.powertools.testutils.logging.InvocationLogs.Level.INFO; + +public class LoggingE2ET { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static Infrastructure infrastructure; + private static String functionName; + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + public static void setup() { + infrastructure = Infrastructure.builder() + .testName(LoggingE2ET.class.getSimpleName()) + .pathToFunction("logging") + .environmentVariables( + Stream.of(new String[][]{ + {"POWERTOOLS_LOG_LEVEL", "INFO"}, + {"POWERTOOLS_SERVICE_NAME", LoggingE2ET.class.getSimpleName()} + }) + .collect(Collectors.toMap(data -> data[0], data -> data[1]))) + .build(); + functionName = infrastructure.deploy(); + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) + infrastructure.destroy(); + } + + @Test + public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { + // GIVEN + String orderId = UUID.randomUUID().toString(); + String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId +"\"}}"; + + // WHEN + InvocationResult invocationResult1 = invokeFunction(functionName, event); + InvocationResult invocationResult2 = invokeFunction(functionName, event); + + // THEN + String[] functionLogs = invocationResult1.getLogs().getFunctionLogs(INFO); + assertThat(functionLogs).hasSize(1); + + 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("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(); + } +} 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 new file mode 100644 index 000000000..4b8d7ea5a --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java @@ -0,0 +1,73 @@ +package software.amazon.lambda.powertools; + +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.metrics.MetricsFetcher; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +public 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() { + infrastructure = Infrastructure.builder() + .testName(MetricsE2ET.class.getSimpleName()) + .pathToFunction("metrics") + .environmentVariables( + Stream.of(new String[][]{ + {"POWERTOOLS_METRICS_NAMESPACE", namespace}, + {"POWERTOOLS_SERVICE_NAME", service} + }) + .collect(Collectors.toMap(data -> data[0], data -> data[1]))) + .build(); + functionName = infrastructure.deploy(); + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) + infrastructure.destroy(); + } + + @Test + public void test_recordMetrics() { + // GIVEN + String event1 = "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, event1); + + // 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]))); + 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); + } +} 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 new file mode 100644 index 000000000..1f002fe60 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java @@ -0,0 +1,88 @@ +package software.amazon.lambda.powertools; + +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; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +public class TracingE2ET { + private static final String service = "TracingE2EService_" + UUID.randomUUID(); // "TracingE2EService_e479fb27-422b-4107-9f8c-086c62e1cd12"; + + private static Infrastructure infrastructure; + private static String functionName; + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + public static void setup() { + infrastructure = Infrastructure.builder() + .testName(TracingE2ET.class.getSimpleName()) + .pathToFunction("tracing") + .tracing(true) + .environmentVariables(Collections.singletonMap("POWERTOOLS_SERVICE_NAME", service)) + .build(); + functionName = infrastructure.deploy(); + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) + infrastructure.destroy(); + } + + @Test + public void test_tracing() { + // GIVEN + String message = "Hello World"; + String event = String.format("{\"message\":\"%s\"}", message); + String result = String.format("%s (%s)", message, functionName); + + // WHEN + InvocationResult invocationResult = invokeFunction(functionName, event); + + // THEN + Trace trace = TraceFetcher.builder() + .start(invocationResult.getStart()) + .end(invocationResult.getEnd()) + .functionName(functionName) + .build() + .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); + assertThat(sub.getName()).isIn("## internal_stuff", "## buildMessage"); + + sub = handleRequest.getSubsegments().get(1); + assertThat(sub.getName()).isIn("## internal_stuff", "## buildMessage"); + + SubSegment buildMessage = handleRequest.getSubsegments().stream().filter(subSegment -> subSegment.getName().equals("## buildMessage")).findFirst().orElse(null); + assertThat(buildMessage).isNotNull(); + assertThat(buildMessage.getAnnotations()).hasSize(1); + assertThat(buildMessage.getAnnotations().get("message")).isEqualTo(message); + assertThat(buildMessage.getMetadata()).hasSize(1); + metadata = (Map<String, Object>) buildMessage.getMetadata().get(service); + assertThat(metadata.get("buildMessage response")).isEqualTo(result); + } +} 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 new file mode 100644 index 000000000..f3659e5c7 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java @@ -0,0 +1,355 @@ +package software.amazon.lambda.powertools.testutils; + +import com.fasterxml.jackson.databind.JsonNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.Yaml; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.*; +import software.amazon.awscdk.cxapi.CloudAssembly; +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.Table; +import software.amazon.awscdk.services.lambda.Code; +import software.amazon.awscdk.services.lambda.Function; +import software.amazon.awscdk.services.lambda.Tracing; +import software.amazon.awscdk.services.logs.LogGroup; +import software.amazon.awscdk.services.logs.RetentionDays; +import software.amazon.awscdk.services.s3.assets.AssetOptions; +import software.amazon.awssdk.core.waiters.WaiterResponse; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.cloudformation.CloudFormationClient; +import software.amazon.awssdk.services.cloudformation.model.*; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.Collections.singletonList; + +/** + * This class is in charge of bootstrapping the infrastructure for the tests. + * <br/> + * Tests are actually run on AWS, so we need to provision Lambda functions, DynamoDB table (for Idempotency), + * 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`) + * and the CloudFormation stack is created (with the SDK `createStack`) + */ +public class Infrastructure { + private static final Logger LOG = LoggerFactory.getLogger(Infrastructure.class); + + private final String stackName; + private final boolean tracing; + private final Map<String, String> envVar; + private final JavaRuntime runtime; + private final App app; + private final Stack stack; + private final long timeout; + private final String pathToFunction; + private final S3Client s3; + private final CloudFormationClient cfn; + private final Region region; + private final String account; + private final String idempotencyTable; + private String functionName; + private Object cfnTemplate; + private String cfnAssetDirectory; + private final SdkHttpClient httpClient; + + private Infrastructure(Builder builder) { + this.stackName = builder.stackName; + this.tracing = builder.tracing; + this.envVar = builder.environmentVariables; + this.runtime = builder.runtime; + this.timeout = builder.timeoutInSeconds; + this.pathToFunction = builder.pathToFunction; + this.idempotencyTable = builder.idemPotencyTable; + + this.app = new App(); + this.stack = createStackWithLambda(); + + this.synthesize(); + + this.httpClient = UrlConnectionHttpClient.builder().build(); + this.region = Region.of(System.getProperty("AWS_DEFAULT_REGION", "eu-west-1")); + this.account = StsClient.builder() + .httpClient(httpClient) + .region(region) + .build().getCallerIdentity().account(); + + s3 = S3Client.builder() + .httpClient(httpClient) + .region(region) + .build(); + cfn = CloudFormationClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + } + + /** + * Use the CloudFormation SDK to create the stack + * @return the name of the function deployed part of the stack + */ + public String deploy() { + uploadAssets(); + LOG.info("Deploying '" + stackName + "' on account " + account); + cfn.createStack(CreateStackRequest.builder() + .stackName(stackName) + .templateBody(new Yaml().dump(cfnTemplate)) + .timeoutInMinutes(10) + .onFailure(OnFailure.ROLLBACK) + .capabilities(Capability.CAPABILITY_IAM) + .build()); + WaiterResponse<DescribeStacksResponse> waiterResponse = cfn.waiter().waitUntilStackCreateComplete(DescribeStacksRequest.builder().stackName(stackName).build()); + if (waiterResponse.matched().response().isPresent()) { + LOG.info("Stack " + waiterResponse.matched().response().get().stacks().get(0).stackName() + " successfully deployed"); + } else { + throw new RuntimeException("Failed to create stack"); + } + return functionName; + } + + /** + * Destroy the CloudFormation stack + */ + public void destroy() { + LOG.info("Deleting '" + stackName + "' on account " + account); + cfn.deleteStack(DeleteStackRequest.builder().stackName(stackName).build()); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + public long timeoutInSeconds = 30; + public String pathToFunction; + public String testName; + private String stackName; + private boolean tracing = false; + private JavaRuntime runtime; + private Map<String, String> environmentVariables = new HashMap<>(); + private String idemPotencyTable; + + private Builder() { + getJavaRuntime(); + } + + /** + * 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 + if (javaVersion == null) { + throw new IllegalArgumentException("JAVA_VERSION is not set"); + } + if (javaVersion.startsWith("8")) { + runtime = JavaRuntime.JAVA8AL2; + } else if (javaVersion.startsWith("11")) { + runtime = JavaRuntime.JAVA11; + } else { + throw new IllegalArgumentException("Unsupported Java version " + javaVersion); + } + LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime()); + } + + public Infrastructure build() { + Objects.requireNonNull(testName, "testName must not be null"); + + String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12); + stackName = testName + "-" + uuid; + + Objects.requireNonNull(pathToFunction, "pathToFunction must not be null"); + return new Infrastructure(this); + } + + public Builder testName(String testName) { + this.testName = testName; + return this; + } + + public Builder pathToFunction(String pathToFunction) { + this.pathToFunction = pathToFunction; + return this; + } + + public Builder tracing(boolean tracing) { + this.tracing = tracing; + return this; + } + + public Builder idempotencyTable(String tableName) { + this.idemPotencyTable = tableName; + return this; + } + + public Builder environmentVariables(Map<String, String> environmentVariables) { + this.environmentVariables = environmentVariables; + return this; + } + + public Builder timeoutInSeconds(long timeoutInSeconds) { + this.timeoutInSeconds = timeoutInSeconds; + return this; + } + } + + /** + * Build the CDK Stack containing the required resources (Lambda function, LogGroup, DDB Table) + * @return the CDK stack + */ + private Stack createStackWithLambda() { + 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"; + + LOG.debug("Building Lambda function with command "+ packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); + Function function = Function.Builder + .create(stack, functionName) + .code(Code.fromAsset("handlers/", AssetOptions.builder() + .bundling(builderOptions + .command(packagingInstruction) + .build()) + .build())) + .functionName(functionName) + .handler("software.amazon.lambda.powertools.e2e.Function::handleRequest") + .memorySize(1024) + .timeout(Duration.seconds(timeout)) + .runtime(runtime.getCdkRuntime()) + .environment(envVar) + .tracing(tracing ? Tracing.ACTIVE : Tracing.DISABLED) + .build(); + + LogGroup.Builder + .create(stack, functionName + "-logs") + .logGroupName("/aws/lambda/" + functionName) + .retention(RetentionDays.ONE_DAY) + .removalPolicy(RemovalPolicy.DESTROY) + .build(); + + if (!StringUtils.isEmpty(idempotencyTable)) { + Table table = Table.Builder + .create(stack, "IdempotencyTable") + .billingMode(BillingMode.PAY_PER_REQUEST) + .removalPolicy(RemovalPolicy.DESTROY) + .partitionKey(Attribute.builder().name("id").type(AttributeType.STRING).build()) + .tableName(idempotencyTable) + .timeToLiveAttribute("expiration") + .build(); + table.grantReadWriteData(function); + } + + return stack; + } + + /** + * cdk synth to retrieve the CloudFormation template and assets directory + */ + private void synthesize() { + CloudAssembly synth = app.synth(); + cfnTemplate = synth.getStackByName(stack.getStackName()).getTemplate(); + cfnAssetDirectory = synth.getDirectory(); + } + + /** + * Upload assets (mainly lambda function jars) to S3 + */ + private void uploadAssets() { + Map<String, Asset> assets = findAssets(); + assets.forEach((objectKey, asset) -> { + if (!asset.assetPath.endsWith(".jar")) { + return; + } + 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"); + return; + } + LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); + s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), Paths.get(cfnAssetDirectory, asset.assetPath)); + }); + } + + /** + * Reading the cdk assets.json file to retrieve the list of assets to push to S3 + * @return a map of assets + */ + private Map<String, Asset> findAssets() { + Map<String, Asset> assets = new HashMap<>(); + try { + JsonNode jsonNode = JsonConfig.get().getObjectMapper().readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); + JsonNode files = jsonNode.get("files"); + 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(); + Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account).replace("${AWS::Region}", region.toString())); + assets.put(objectKey, asset); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + return assets; + } + + private static class Asset { + private final String assetPath; + private final String assetPackaging; + private final String bucketName; + + Asset(String assetPath, String assetPackaging, String bucketName) { + this.assetPath = assetPath; + this.assetPackaging = assetPackaging; + this.bucketName = bucketName; + } + + @Override + public String toString() { + return "Asset{" + + "assetPath='" + assetPath + '\'' + + ", assetPackaging='" + assetPackaging + '\'' + + ", bucketName='" + bucketName + '\'' + + '}'; + } + } +} 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 new file mode 100644 index 000000000..94ec13518 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java @@ -0,0 +1,37 @@ +package software.amazon.lambda.powertools.testutils; + +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"); + + private final String runtime; + private final Runtime cdkRuntime; + + private final String mvnProperty; + + JavaRuntime(String runtime, Runtime cdkRuntime, String mvnProperty) { + this.runtime = runtime; + this.cdkRuntime = cdkRuntime; + this.mvnProperty = mvnProperty; + } + + public Runtime getCdkRuntime() { + return cdkRuntime; + } + + public String getRuntime() { + return runtime; + } + + @Override + public String toString() { + return runtime; + } + + public String getMvnProperty() { + return mvnProperty; + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java new file mode 100644 index 000000000..168fec71b --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java @@ -0,0 +1,43 @@ +package software.amazon.lambda.powertools.testutils.lambda; + +import software.amazon.awssdk.services.lambda.model.InvokeResponse; +import software.amazon.lambda.powertools.testutils.logging.InvocationLogs; + +import java.time.Instant; + +public class InvocationResult { + + private final InvocationLogs logs; + private final String result; + + private final String requestId; + private final Instant start; + private final Instant end; + + public InvocationResult(InvokeResponse response, Instant start, Instant end) { + requestId = response.responseMetadata().requestId(); + logs = new InvocationLogs(response.logResult(), requestId); + result = response.payload().asUtf8String(); + this.start = start; + this.end = end; + } + public InvocationLogs getLogs() { + return logs; + } + + public String getResult() { + return result; + } + + public String getRequestId() { + return requestId; + } + + public Instant getStart() { + return start; + } + + public Instant getEnd() { + return end; + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java new file mode 100644 index 000000000..ecde1042e --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java @@ -0,0 +1,39 @@ +package software.amazon.lambda.powertools.testutils.lambda; + +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.lambda.LambdaClient; +import software.amazon.awssdk.services.lambda.model.InvokeRequest; +import software.amazon.awssdk.services.lambda.model.InvokeResponse; +import software.amazon.awssdk.services.lambda.model.LogType; + +import java.time.Clock; +import java.time.Instant; + +import static java.time.temporal.ChronoUnit.MINUTES; + +public class LambdaInvoker { + 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 final LambdaClient lambda = LambdaClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + + public static InvocationResult invokeFunction(String functionName, String input) { + SdkBytes payload = SdkBytes.fromUtf8String(input); + + InvokeRequest request = InvokeRequest.builder() + .functionName(functionName) + .payload(payload) + .logType(LogType.TAIL) + .build(); + + Instant start = Instant.now(Clock.systemUTC()).truncatedTo(MINUTES); + InvokeResponse response = lambda.invoke(request); + Instant end = start.plus(1, MINUTES); + return new InvocationResult(response, start, end); + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java new file mode 100644 index 000000000..1ae1cfad7 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java @@ -0,0 +1,71 @@ +package software.amazon.lambda.powertools.testutils.logging; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.stream.IntStream; + +/** + * Logs for a specific Lambda invocation + */ +public class InvocationLogs { + private final String[] logs; + private final String[] functionLogs; + + public InvocationLogs(String base64Logs, String requestId) { + String rawLogs = new String(Base64.getDecoder().decode(base64Logs), StandardCharsets.UTF_8); + this.logs = rawLogs.split("\n"); + + String start = String.format("START RequestId: %s", requestId); + String end = String.format("END RequestId: %s", requestId); + int startPos = IntStream.range(0, logs.length) + .filter(i -> logs[i].startsWith(start)) + .findFirst() + .orElse(-1); + int endPos = IntStream.range(0, logs.length) + .filter(i -> logs[i].equals(end)) + .findFirst() + .orElse(-1); + this.functionLogs = Arrays.copyOfRange(this.logs, startPos + 1, endPos); + } + + public String[] getAllLogs() { + return logs; + } + + /** + * Return only logs from function, exclude START, END, and REPORT and other elements generated by Lambda service + * @return only logs generated by the function + */ + public String[] getFunctionLogs() { + return this.functionLogs; + } + + public String[] getFunctionLogs(Level level) { + String[] filtered = getFunctionLogs(); + + return Arrays.stream(filtered).filter(log -> log.contains("\"level\":\""+level.getLevel()+"\"")).toArray(String[]::new); + } + + public enum Level { + DEBUG("DEBUG"), + INFO("INFO"), + WARN("WARN"), + ERROR("ERROR"); + + private final String level; + + Level(String lvl) { + this.level = lvl; + } + + public String getLevel() { + return level; + } + + @Override + public String toString() { + return level; + } + } +} 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 new file mode 100644 index 000000000..eb3cd63a4 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java @@ -0,0 +1,95 @@ +package software.amazon.lambda.powertools.testutils.metrics; + +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 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; +import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; +import software.amazon.awssdk.services.cloudwatch.model.*; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import static java.time.Duration.ofSeconds; + +/** + * Class in charge of retrieving the actual metrics of a Lambda execution on CloudWatch + */ +public class MetricsFetcher { + private static final Logger LOG = LoggerFactory.getLogger(MetricsFetcher.class); + + 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 final CloudWatchClient cloudwatch = CloudWatchClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + + /** + * 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 + * @param end + * @param period + * @param namespace + * @param metricName + * @param dimensions + * @return + */ + public List<Double> fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, 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 = () -> { + 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()) + .metricStat(MetricStat.builder() + .unit(StandardUnit.COUNT) + .metric(Metric.builder() + .namespace(namespace) + .metricName(metricName) + .dimensions(dimensionsList) + .build()) + .period(period) + .stat("Sum") + .build()) + .returnData(true) + .build()) + .build()); + List<Double> values = metricData.metricDataResults().get(0).values(); + if (values == null || values.isEmpty()) { + throw new Exception("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(); + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java new file mode 100644 index 000000000..08f4bf7d8 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java @@ -0,0 +1,189 @@ +package software.amazon.lambda.powertools.testutils.tracing; + +import com.fasterxml.jackson.annotation.JsonSetter; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class SegmentDocument { + private String id; + + @JsonSetter("trace_id") + private String traceId; + + private String name; + + @JsonSetter("start_time") + private long startTime; + + @JsonSetter("end_time") + private long endTime; + + private String origin; + + private List<SubSegment> subsegments = new ArrayList<>(); + + public SegmentDocument() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTraceId() { + return traceId; + } + + public void setTraceId(String traceId) { + this.traceId = traceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getEndTime() { + return endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public List<SubSegment> getSubsegments() { + return subsegments; + } + + public void setSubsegments(List<SubSegment> subsegments) { + this.subsegments = subsegments; + } + + public Duration getDuration() { + return Duration.ofMillis(endTime - startTime); + } + + public boolean hasSubsegments() { + return !subsegments.isEmpty(); + } + + public static class SubSegment{ + private String id; + + private String name; + + @JsonSetter("start_time") + private long startTime; + + @JsonSetter("end_time") + private long endTime; + + private List<SubSegment> subsegments = new ArrayList<>(); + + private Map<String, Object> annotations; + + private Map<String, Object> metadata; + + private String namespace; + + public SubSegment() { + } + + public boolean hasSubsegments() { + return !subsegments.isEmpty(); + } + + public Duration getDuration() { + return Duration.ofMillis(endTime - startTime); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getEndTime() { + return endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public List<SubSegment> getSubsegments() { + return subsegments; + } + + public void setSubsegments(List<SubSegment> subsegments) { + this.subsegments = subsegments; + } + + public Map<String, Object> getAnnotations() { + return annotations; + } + + public void setAnnotations(Map<String, Object> annotations) { + this.annotations = annotations; + } + + public Map<String, Object> getMetadata() { + return metadata; + } + + public void setMetadata(Map<String, Object> metadata) { + this.metadata = metadata; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + } +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java new file mode 100644 index 000000000..15026a9d1 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java @@ -0,0 +1,21 @@ +package software.amazon.lambda.powertools.testutils.tracing; + +import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; + +import java.util.ArrayList; +import java.util.List; + +public class Trace { + private final List<SubSegment> subsegments = new ArrayList<>(); + + public Trace() { + } + + public List<SubSegment> getSubsegments() { + return subsegments; + } + + public void addSubSegment(SubSegment subSegment) { + subsegments.add(subSegment); + } +} 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 new file mode 100644 index 000000000..e7cd13640 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java @@ -0,0 +1,210 @@ +package software.amazon.lambda.powertools.testutils.tracing; + +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 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; +import software.amazon.awssdk.services.xray.XRayClient; +import software.amazon.awssdk.services.xray.model.*; +import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; + +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.stream.Collectors; + +import static java.time.Duration.ofSeconds; + +/** + * 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 Logger LOG = LoggerFactory.getLogger(TraceFetcher.class); + + private final Instant start; + private final Instant end; + private final String filterExpression; + private final List<String> excludedSegments; + + /** + * @param start beginning of the time slot to search in + * @param end end of the time slot to search in + * @param filterExpression eventual filter for the search + * @param excludedSegments list of segment to exclude from the search + */ + public TraceFetcher(Instant start, Instant end, String filterExpression, List<String> excludedSegments) { + this.start = start; + this.end = end; + this.filterExpression = filterExpression; + this.excludedSegments = excludedSegments; + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Retrieve the traces corresponding to a specific function during a specific time slot. + * Use a retry mechanism as traces may not be available instantaneously after a function runs. + * + * @return traces + */ + public Trace fetchTrace() { + Callable<Trace> callable = () -> { + List<String> traceIds = getTraceIds(); + return getTrace(traceIds); + }; + + RetryConfig retryConfig = new RetryConfigBuilder() + .withMaxNumberOfTries(10) + .retryOnAnyException() + .withDelayBetweenTries(ofSeconds(5)) + .withRandomExponentialBackoff() + .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(); + } + + /** + * Retrieve traces from trace ids. + * @param traceIds + * @return + */ + private Trace getTrace(List<String> traceIds) { + BatchGetTracesResponse tracesResponse = xray.batchGetTraces(BatchGetTracesRequest.builder() + .traceIds(traceIds) + .build()); + if (!tracesResponse.hasTraces()) { + throw new RuntimeException("No trace found"); + } + Trace traceRes = new Trace(); + tracesResponse.traces().forEach(trace -> { + if (trace.hasSegments()) { + 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()); + } + } + } catch (JsonProcessingException e) { + LOG.error("Failed to parse segment document: " + e.getMessage()); + throw new RuntimeException(e); + } + }); + } + }); + return traceRes; + } + + private void getNestedSubSegments(List<SubSegment> subsegments, Trace traceRes, List<String> idsToIgnore) { + subsegments.forEach(subsegment -> { + List<String> subSegmentIdsToIgnore = Collections.emptyList(); + if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { + traceRes.addSubSegment(subsegment); + if (subsegment.hasSubsegments()) { + subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId).collect(Collectors.toList()); + } + } + if (subsegment.hasSubsegments()) { + getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); + } + }); + } + + /** + * Use the X-Ray SDK to retrieve the trace ids corresponding to a specific function during a specific time slot + * @return a list of trace ids + */ + private List<String> getTraceIds() { + GetTraceSummariesResponse traceSummaries = xray.getTraceSummaries(GetTraceSummariesRequest.builder() + .startTime(start) + .endTime(end) + .timeRangeType(TimeRangeType.EVENT) + .sampling(false) + .filterExpression(filterExpression) + .build()); + if (!traceSummaries.hasTraceSummaries()) { + throw new RuntimeException("No trace id found"); + } + List<String> traceIds = traceSummaries.traceSummaries().stream().map(TraceSummary::id).collect(Collectors.toList()); + if (traceIds.isEmpty()) { + throw new RuntimeException("No trace id found"); + } + return traceIds; + } + + 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 final XRayClient xray = XRayClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + + public static class Builder { + private Instant start; + private Instant end; + private String filterExpression; + private List<String> excludedSegments = Arrays.asList("Initialization", "Invocation", "Overhead"); + + public TraceFetcher build() { + if (filterExpression == null) + throw new IllegalArgumentException("filterExpression or functionName is required"); + if (start == null) + throw new IllegalArgumentException("start is required"); + 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); + } + + public Builder start(Instant start) { + this.start = start; + return this; + } + + public Builder end(Instant end) { + this.end = end; + return this; + } + + public Builder filterExpression(String filterExpression) { + this.filterExpression = filterExpression; + return this; + } + + /** + * "Initialization", "Invocation", "Overhead" are excluded by default + * @param excludedSegments + * @return + */ + public Builder excludeSegments(List<String> excludedSegments) { + this.excludedSegments = excludedSegments; + return this; + } + + public Builder functionName(String functionName) { + this.filterExpression = String.format("service(id(name: \"%s\", type: \"AWS::Lambda::Function\"))", functionName); + return this; + } + } +} diff --git a/powertools-e2e-tests/src/test/resources/logback-test.xml b/powertools-e2e-tests/src/test/resources/logback-test.xml new file mode 100644 index 000000000..aac638007 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/logback-test.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration> + +<configuration> + <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/> + <import class="ch.qos.logback.core.ConsoleAppender"/> + + <appender name="STDOUT" class="ConsoleAppender"> + <encoder class="PatternLayoutEncoder"> + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <logger name="software.amazon.lambda.powertools" level="DEBUG"/> + + <root level="info"> + <appender-ref ref="STDOUT"/> + </root> + +</configuration> \ No newline at end of file From 94bed9bcc5153822ee33d90f508c21e51736ba96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:19:18 +0100 Subject: [PATCH 0274/1008] build(deps): bump aws.sdk.version from 2.20.27 to 2.20.28 (#1105) Bumps `aws.sdk.version` from 2.20.27 to 2.20.28. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 132bd7f38..f5963ef17 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.27</version> + <version>2.20.28</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 8f2701054..b4cdbcec2 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.27</aws.sdk.version> + <aws.sdk.version>2.20.28</aws.sdk.version> <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From cf27a057f66bb0c3b7bb8553634bc9d336641ce6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:19:56 +0100 Subject: [PATCH 0275/1008] build(deps-dev): bump constructs from 10.1.138 to 10.1.284 (#1109) Bumps [constructs](https://github.com/aws/constructs) from 10.1.138 to 10.1.284. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 77e9fa458..ca93a927e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> - <constructs.version>10.1.138</constructs.version> + <constructs.version>10.1.284</constructs.version> <cdk.version>2.47.0</cdk.version> </properties> From 280512682780a1382564e929d95c4b489c1fdce4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:20:40 +0100 Subject: [PATCH 0276/1008] build(deps): bump logback-classic from 1.2.6 to 1.4.6 (#1107) Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.2.6 to 1.4.6. --- pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b4cdbcec2..e9a7f2c1b 100644 --- a/pom.xml +++ b/pom.xml @@ -286,7 +286,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.2.6</version> + <version>1.4.6</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index ca93a927e..518ac1183 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.4.4</version> + <version>1.4.6</version> </dependency> <dependency> From 67a1d4e93a71e4e40e9fd3150981efe4f3586624 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden <jeromevdl@gmail.com> Date: Tue, 21 Mar 2023 14:33:42 +0100 Subject: [PATCH 0277/1008] run build when e2e are updated / do not deploy e2e tests on maven central --- .github/workflows/build.yml | 2 ++ powertools-e2e-tests/pom.xml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db25e4164..9ffc7b024 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ on: - 'powertools-parameters/**' - 'powertools-metrics/**' - 'powertools-test-suite/**' + - 'powertools-e2e-tests/**' - 'examples/**' - 'pom.xml' - 'examples/pom.xml' @@ -35,6 +36,7 @@ on: - 'powertools-parameters/**' - 'powertools-metrics/**' - 'powertools-test-suite/**' + - 'powertools-e2e-tests/**' - 'examples/**' - 'pom.xml' - 'examples/pom.xml' diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 518ac1183..91f3d969d 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -19,8 +19,12 @@ <maven.compiler.target>8</maven.compiler.target> <constructs.version>10.1.284</constructs.version> <cdk.version>2.47.0</cdk.version> + + <!-- Don't deploy the e2e tests --> + <maven.deploy.skip>true</maven.deploy.skip> </properties> + <dependencies> <dependency> <groupId>ch.qos.logback</groupId> From 21bb0a32ce8ca43515fb3ecaaeb0c704fe477ffc Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden <jeromevdl@gmail.com> Date: Tue, 21 Mar 2023 15:03:01 +0100 Subject: [PATCH 0278/1008] automerge on dependabot --- .github/workflows/auto-merge.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 198ca5826..18b4d7328 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -36,7 +36,7 @@ jobs: var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - run: unzip pr.zip - - name: Merge me! + - name: Create review uses: actions/github-script@v3 with: script: | @@ -49,10 +49,12 @@ jobs: 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 }} \ No newline at end of file + + github-token: ${{ secrets.AUTOMERGE }} + - name: Merge me! + uses: peter-evans/create-or-update-comment@v2 # use dependabot comments to automatically merge + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + dependabot squash and merge + reactions: rocket From 59a502b2b64808a2e2dc2b85432334add223304a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 15:25:10 +0100 Subject: [PATCH 0279/1008] build(deps-dev): bump snakeyaml in /powertools-e2e-tests (#1110) Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.33 to 2.0. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 91f3d969d..b1f48ad6f 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -118,7 +118,7 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>1.33</version> + <version>2.0</version> <scope>test</scope> </dependency> <dependency> From 9c385d626348aec656ce349c3b8f360c4bf56a93 Mon Sep 17 00:00:00 2001 From: Ruben Fonseca <fonseka@gmail.com> Date: Tue, 25 Apr 2023 15:46:31 +0200 Subject: [PATCH 0280/1008] chore(ci): add workflow to dispatch analytics fetching (#1143) --- .github/workflows/dispatch_analytics.yml | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/dispatch_analytics.yml diff --git a/.github/workflows/dispatch_analytics.yml b/.github/workflows/dispatch_analytics.yml new file mode 100644 index 000000000..49a276f6f --- /dev/null +++ b/.github/workflows/dispatch_analytics.yml @@ -0,0 +1,43 @@ +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 From b7c5c89cbc4965217098a3366183d7a3c0071e35 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 3 May 2023 22:28:27 +0200 Subject: [PATCH 0281/1008] Create docs_beta.yml --- .github/workflows/docs_beta.yml | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/docs_beta.yml diff --git a/.github/workflows/docs_beta.yml b/.github/workflows/docs_beta.yml new file mode 100644 index 000000000..91d0719c2 --- /dev/null +++ b/.github/workflows/docs_beta.yml @@ -0,0 +1,41 @@ +name: Docs + +on: + pull_request: + branches: + - docsTest + paths: + - 'docs/**' + push: + branches: + - docsTest + paths: + - 'docs/**' + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - 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 }} + - 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: Deploy Docs + run: | + aws s3 sync \ + dist/* \ + s3://${AWS_DOCS_BUCKET}/java/ From 657fc86d9010d26aedda2c02321a3263b13fc8c5 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 3 May 2023 22:39:07 +0200 Subject: [PATCH 0282/1008] Update docs_beta.yml --- .github/workflows/docs_beta.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs_beta.yml b/.github/workflows/docs_beta.yml index 91d0719c2..a71073ee7 100644 --- a/.github/workflows/docs_beta.yml +++ b/.github/workflows/docs_beta.yml @@ -12,6 +12,10 @@ on: paths: - 'docs/**' +permissions: + id-token: write + contents: read + jobs: docs: runs-on: ubuntu-latest From c82de6306c485c820944ff52a12408bef8439411 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 3 May 2023 23:01:11 +0200 Subject: [PATCH 0283/1008] Update docs_beta.yml --- .github/workflows/docs_beta.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs_beta.yml b/.github/workflows/docs_beta.yml index a71073ee7..7266818dd 100644 --- a/.github/workflows/docs_beta.yml +++ b/.github/workflows/docs_beta.yml @@ -19,6 +19,7 @@ permissions: jobs: docs: runs-on: ubuntu-latest + environment: Docs steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef From 768a9dae281d192ca654b5c57481ddec7fb5119c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 14:08:31 +0200 Subject: [PATCH 0284/1008] build(deps): bump maven-failsafe-plugin from 2.22.2 to 3.1.0 (#1154) Bumps [maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 2.22.2 to 3.1.0. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index b1f48ad6f..3cfb8aae5 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -156,7 +156,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>2.22.2</version> + <version>3.1.0</version> <executions> <execution> <goals> From 48868e59c1210a2cc6a7e329f1c17e1f3570ae68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 14:09:07 +0200 Subject: [PATCH 0285/1008] build(deps-dev): bump constructs from 10.1.284 to 10.2.25 (#1162) Bumps [constructs](https://github.com/aws/constructs) from 10.1.284 to 10.2.25. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 3cfb8aae5..e4cb4f656 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> - <constructs.version>10.1.284</constructs.version> + <constructs.version>10.2.25</constructs.version> <cdk.version>2.47.0</cdk.version> <!-- Don't deploy the e2e tests --> From d9cd971836b74585ad4a6086ec5836018a0934ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 14:10:31 +0200 Subject: [PATCH 0286/1008] build(deps): bump maven-surefire-plugin from 2.22.0 to 3.1.0 (#1153) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 2.22.0 to 3.1.0. --- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 4 ++-- examples/powertools-examples-serialization/pom.xml | 4 ++-- examples/powertools-examples-validation/pom.xml | 4 ++-- pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 64ef72ac3..fdd29aab5 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -127,7 +127,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.0.0-M5</version> + <version>3.1.0</version> <configuration> <systemPropertyVariables> <sqlite4java.library.path>${project.build.directory}/native-libs</sqlite4java.library.path> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index aca45508a..d87a91c97 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -59,8 +59,8 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 2.22.0 or higher --> - <version>2.22.0</version> + <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> + <version>3.1.0</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index a02c32820..9fbdf593d 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -59,8 +59,8 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 2.22.0 or higher --> - <version>2.22.0</version> + <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> + <version>3.1.0</version> </plugin> </plugins> </build> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 13c2ff816..e9a8253b4 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -54,8 +54,8 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 2.22.0 or higher --> - <version>2.22.0</version> + <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> + <version>3.1.0</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> diff --git a/pom.xml b/pom.xml index e9a7f2c1b..a9f599cb4 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version> - <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version> + <maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 4fc2ff4a1..ead13344b 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -193,7 +193,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.0.0-M5</version> + <version>3.1.0</version> <configuration> <argLine> --add-opens java.base/java.util=ALL-UNNAMED From 7352122b45e6f5c783146b0f8a4746682b9fd618 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 14:11:17 +0200 Subject: [PATCH 0287/1008] build(deps): bump aws.sdk.version from 2.20.28 to 2.20.29 (#1111) Bumps `aws.sdk.version` from 2.20.28 to 2.20.29. --- .github/workflows/docs.yml | 16 +++++++++ .github/workflows/docs_beta.yml | 46 ------------------------ examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 18 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/docs_beta.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d19c64b2d..0d0b9460f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,9 +6,15 @@ on: - 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 @@ -28,3 +34,13 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./dist + - 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 }}/java/ diff --git a/.github/workflows/docs_beta.yml b/.github/workflows/docs_beta.yml deleted file mode 100644 index 7266818dd..000000000 --- a/.github/workflows/docs_beta.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Docs - -on: - pull_request: - branches: - - docsTest - paths: - - 'docs/**' - push: - branches: - - docsTest - paths: - - 'docs/**' - -permissions: - id-token: write - contents: read - -jobs: - docs: - runs-on: ubuntu-latest - environment: Docs - steps: - - 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 }} - - 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: Deploy Docs - run: | - aws s3 sync \ - dist/* \ - s3://${AWS_DOCS_BUCKET}/java/ diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index f5963ef17..0c6a18fcf 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.28</version> + <version>2.20.29</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index a9f599cb4..b8703bebb 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.28</aws.sdk.version> + <aws.sdk.version>2.20.29</aws.sdk.version> <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From ca0653976271886f7f2d6be15b342a8692fa8fe5 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 17 May 2023 11:38:07 +0200 Subject: [PATCH 0288/1008] feat(docs): adds S3 Docs uploader --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0d0b9460f..b8eeab7e7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -43,4 +43,4 @@ jobs: run: | aws s3 sync \ dist \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/java/ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/ From ac0201ece93c3d07852835a78663ed54515607a3 Mon Sep 17 00:00:00 2001 From: Larry Gouger <lgouger@gmail.com> Date: Thu, 18 May 2023 16:58:50 -0400 Subject: [PATCH 0289/1008] Update validation.md (#1167) Fix goupId of dependency --- docs/utilities/validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 28d8c7a20..c51f905aa 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -20,7 +20,7 @@ To install this utility, add the following dependency to your project. <dependencies> ... <dependency> - <groupId>com.amazonaws</groupId> + <groupId>software.amazon.lambda</groupId> <artifactId>powertools-validation</artifactId> <version>{{ powertools.version }}</version> </dependency> @@ -270,4 +270,4 @@ If you need to configure the Jackson ObjectMapper, you can use the `ValidationCo return "OK"; } } - ``` \ No newline at end of file + ``` From ae13f8be126bbfd5904f7de614b8a39a234b4888 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Fri, 2 Jun 2023 10:01:31 +0200 Subject: [PATCH 0290/1008] chore: rename project from Powertools to Powertools for AWS Lambda (Java) (#1169) --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/maintenance.yml | 4 ++-- .github/ISSUE_TEMPLATE/rfc.md | 2 +- .github/ISSUE_TEMPLATE/share_your_work.yml | 8 ++++---- .github/ISSUE_TEMPLATE/support_powertools.yml | 12 ++++++------ CHANGELOG.md | 14 +++++++------- README.md | 6 +++--- docs/FAQs.md | 4 ++-- docs/core/logging.md | 2 +- docs/core/tracing.md | 4 ++-- docs/index.md | 12 ++++++------ docs/utilities/custom_resources.md | 2 +- docs/utilities/idempotency.md | 6 +++--- docs/utilities/serialization.md | 6 +++--- examples/README.md | 2 +- examples/pom.xml | 4 ++-- examples/powertools-examples-core/README.md | 4 ++-- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-core/template.yaml | 2 +- examples/powertools-examples-idempotency/README.md | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/README.md | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/README.md | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-sqs/template.yaml | 2 +- examples/powertools-examples-validation/README.md | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 4 ++-- pom.xml | 4 ++-- .../AbstractCustomResourceHandler.java | 2 +- .../lambda/powertools/cloudformation/Response.java | 4 ++-- powertools-core/pom.xml | 4 ++-- powertools-e2e-tests/README.md | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- powertools-e2e-tests/handlers/logging/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-idempotency/pom.xml | 4 ++-- .../idempotency/internal/IdempotencyHandler.java | 8 ++++---- powertools-logging/pom.xml | 4 ++-- powertools-metrics/pom.xml | 4 ++-- powertools-parameters/pom.xml | 4 ++-- .../powertools/parameters/DynamoDbProvider.java | 2 +- powertools-serialization/pom.xml | 4 ++-- powertools-sqs/pom.xml | 4 ++-- powertools-test-suite/pom.xml | 6 +++--- powertools-tracing/pom.xml | 4 ++-- .../amazon/lambda/powertools/tracing/Tracing.java | 2 +- powertools-validation/pom.xml | 4 ++-- 50 files changed, 97 insertions(+), 97 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c1b7490eb..42170fa86 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -34,7 +34,7 @@ assignees: '' ## Environment -* **Powertools version used**: +* **Powertools for AWS Lambda (Java) version used**: * **Packaging format (Layers, Maven/Gradle)**: * **AWS Lambda function runtime:** * **Debugging logs** diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml index 466feed7b..990c84507 100644 --- a/.github/ISSUE_TEMPLATE/maintenance.yml +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -55,9 +55,9 @@ body: attributes: label: Acknowledgment options: - - label: This request meets [Lambda Powertools Tenets](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) + - label: This request meets [Powertools for AWS Lambda (Java) Tenets](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) required: true - - label: Should this be considered in other Lambda Powertools languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript/) + - label: Should this be considered in other Powertools for AWS Lambda (Java) languages? i.e. [Python](https://github.com/awslabs/aws-lambda-powertools-python/), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript/) required: false - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md index 1db27058d..5a1205884 100644 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ b/.github/ISSUE_TEMPLATE/rfc.md @@ -29,7 +29,7 @@ assignees: '' > This is the bulk of the RFC. -> Explain the design in enough detail for somebody familiar with Powertools to understand it, and for somebody familiar with the implementation to implement it. +> 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. diff --git a/.github/ISSUE_TEMPLATE/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml index 974aec87b..228ee8281 100644 --- a/.github/ISSUE_TEMPLATE/share_your_work.yml +++ b/.github/ISSUE_TEMPLATE/share_your_work.yml @@ -1,5 +1,5 @@ name: I Made This (showcase your work) -description: Share what you did with Powertools 💞💞. Blog post, workshops, presentation, sample apps, etc. +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"] body: @@ -13,7 +13,7 @@ body: description: | Please share the original link to your material. - *Note: Short links will be expanded when added to Powertools documentation* + *Note: Short links will be expanded when added to Powertools for AWS Lambda (Java) documentation* validations: required: true - type: textarea @@ -44,7 +44,7 @@ body: description: | Any notes you might want to share with us related to this material. - *Note: These notes are explicitly to Powertools maintainers. It will not be added to the community resources page.* + *Note: These notes are explicitly to Powertools for AWS Lambda (Java) maintainers. It will not be added to the community resources page.* validations: required: false - type: checkboxes @@ -52,5 +52,5 @@ body: attributes: label: Acknowledgment options: - - label: I understand this content may be removed from Powertools documentation if it doesn't conform with the [Code of Conduct](https://aws.github.io/code-of-conduct) + - 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 diff --git a/.github/ISSUE_TEMPLATE/support_powertools.yml b/.github/ISSUE_TEMPLATE/support_powertools.yml index 362c07af5..2b66d830d 100644 --- a/.github/ISSUE_TEMPLATE/support_powertools.yml +++ b/.github/ISSUE_TEMPLATE/support_powertools.yml @@ -1,6 +1,6 @@ -name: Support Lambda Powertools (become a reference) -description: Add your organization's name or logo to the Lambda Powertools documentation -title: "[Support Lambda Powertools]: <your organization name>" +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"] body: - type: markdown @@ -42,13 +42,13 @@ body: id: use_case attributes: label: (Optional) Use case - description: How are you using Lambda Powertools today? *features, etc.* + description: How are you using Powertools for AWS Lambda (Java) today? *features, etc.* validations: required: false - type: checkboxes id: other_languages attributes: - label: Also using other Lambda Powertools languages? + label: Also using other Powertools for AWS Lambda (Java) languages? options: - label: Python required: false @@ -59,6 +59,6 @@ body: - type: markdown attributes: value: | - *By raising a Support Lambda Powertools 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 (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.* *You can opt-out at any time by commenting or reopening this issue.* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 302f28b87..9e9e6ceb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### Documentation * Improve `powertools-cloudformation` docs (#1090) by @mriccia -* Add link to Lambda powertools workshop (#1095) by @scottgerring +* Add link to Powertools for AWS Lambda (Java) workshop (#1095) by @scottgerring * Fix mdocs and git revision plugin integration (#1066) by @machafer @@ -42,7 +42,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### Documentation -* Docs: Update PowerTools definition by @heitorlessa +* Docs: Update Powertools for AWS Lambda (Java) definition by @heitorlessa * Docs: Add information about other supported langauges to README and docs (#1033) by @kozub ## [1.13.0] - 2022-12-14 @@ -101,8 +101,8 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [1.11.0] - 2022-02-16 ### Added - * Powertools Idempotency module: New module to get your Lambda function [Idempotent](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) (#717) - * Powertools Serialization module: New module to handle JSON (de)serialization (Jackson ObjectMapper, JMESPath functions) + * 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) ## [1.10.3] - 2022-02-01 @@ -151,7 +151,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### Added -* **Powertools 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/awslabs/aws-lambda-powertools-java/pull/560) +* **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/awslabs/aws-lambda-powertools-java/pull/560) * **SQS Large message processing**: Ability to override the default `S3Client` use to fetch payload from S3. [#602](https://github.com/awslabs/aws-lambda-powertools-java/pull/602) ### Regression @@ -168,14 +168,14 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [1.7.2] - 2021-08-03 -* **Powertools All Modules**: Upgrade to the latest(1.14.0) aspectj-maven-plugin which also supports Java 9 and newer versions. +* **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/awslabs/aws-lambda-powertools-java/pull/489) * **Logging**: Performance optimisation to improve cold start. [#484](https://github.com/awslabs/aws-lambda-powertools-java/pull/484) * **SQS Batch processing/Large message**: Module now lazy loads default SQS client. [#484](https://github.com/awslabs/aws-lambda-powertools-java/pull/484) ## [1.7.1] - 2021-07-06 -* **Powertools All Modules**: Fix static code analysis violations done via [spotbugs](https://github.com/spotbugs/spotbugs) ([#458](https://github.com/awslabs/aws-lambda-powertools-java/pull/458)). +* **Powertools for AWS Lambda (Java) All Modules**: Fix static code analysis violations done via [spotbugs](https://github.com/spotbugs/spotbugs) ([#458](https://github.com/awslabs/aws-lambda-powertools-java/pull/458)). ## [1.7.0] - 2021-07-05 diff --git a/README.md b/README.md index 835ddf7f5..b13e05f6c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# AWS Lambda Powertools for Java +# 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/awslabs/aws-lambda-powertools-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/awslabs/aws-lambda-powertools-java/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/awslabs/aws-lambda-powertools-java) -Powertools is a developer toolkit to implement Serverless best practices and increase developer velocity. +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/awslabs/aws-lambda-powertools-python), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet). @@ -11,7 +11,7 @@ Powertools is a developer toolkit to implement Serverless best practices and inc ### Installation -Powertools is available in Maven Central. You can use your favourite dependency management tool to install it +Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it * [maven](https://maven.apache.org/): ```xml diff --git a/docs/FAQs.md b/docs/FAQs.md index e2cfa523e..99ef40905 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -4,7 +4,7 @@ description: Frequently Asked Questions --- -## How can I use Powertools with Lombok? +## 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. @@ -27,7 +27,7 @@ To enable in-place weaving feature you need to use following `aspectj-maven-plug </configuration> ``` -## How can I use Powertools with Kotlin projects? +## 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. diff --git a/docs/core/logging.md b/docs/core/logging.md index f35c88067..708607a9f 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -13,7 +13,7 @@ Logging provides an opinionated logger with output structured as JSON. ## Initialization -Powertools extends the functionality of Log4J. Below is an example `#!xml log4j2.xml` file, with the `JsonTemplateLayout` using `#!json LambdaJsonLayout.json` configured. +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. !!! info "LambdaJsonLayout is now deprecated" diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 550f9614c..13d0be292 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -37,12 +37,12 @@ Before your use this utility, your AWS Lambda function [must have permissions](h POWERTOOLS_SERVICE_NAME: example ``` -The Powertools service name is used as the X-Ray namespace. This can be set using the environment variable +The Powertools for AWS Lambda (Java) service name is used as the X-Ray namespace. This can be set using the environment variable `POWERTOOLS_SERVICE_NAME` ### Lambda handler -To enable Powertools tracing to your function add the `@Tracing` annotation to your `handleRequest` method or on +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. diff --git a/docs/index.md b/docs/index.md index fa2d0fbf4..f595c1b65 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,19 @@ --- title: Homepage -description: AWS Lambda Powertools for Java +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/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) -Powertools 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 suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. ???+ tip - Powertools is also available for [Python](https://awslabs.github.io/aws-lambda-powertools-python/){target="_blank"}, [TypeScript](https://awslabs.github.io/aws-lambda-powertools-typescript/){target="_blank"}, and [.NET](https://awslabs.github.io/aws-lambda-powertools-dotnet/){target="_blank"} + Powertools for AWS Lambda is also available for [Python](https://awslabs.github.io/aws-lambda-powertools-python/){target="_blank"}, [TypeScript](https://awslabs.github.io/aws-lambda-powertools-typescript/){target="_blank"}, and [.NET](https://awslabs.github.io/aws-lambda-powertools-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, - the [AWS Lambda Powertools for Java workshop](https://catalog.us-east-1.prod.workshops.aws/workshops/a7011c82-e4af-4a52-80fa-fcd61f1dacd9/en-US/introduction) is a great next step. + 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,14 +28,14 @@ This project separates core utilities that will be available in other runtimes v ## Install -Powertools dependencies are available in Maven Central. You can use your favourite dependency management tool to install it +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) **Quick hello world examples using SAM CLI** -You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including AWS Lambda Powertools for Java. +You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including Powertools for AWS Lambda (Java). ```bash sam init --location gh:aws-samples/cookiecutter-aws-sam-powertools-java diff --git a/docs/utilities/custom_resources.md b/docs/utilities/custom_resources.md index d9f8494c3..d0c1eacf2 100644 --- a/docs/utilities/custom_resources.md +++ b/docs/utilities/custom_resources.md @@ -103,7 +103,7 @@ In both of the scenarios, powertools-java will return the `physicalResourceId` t #### Why do you need a physicalResourceId? -It is recommended that you always explicitly provide a `physicalResourceId` in your response rather than letting powertools generate if for you because `physicalResourceId` has a crucial role in the lifecycle of a CloudFormation custom resource. +It is recommended that you always explicitly provide a `physicalResourceId` in your response rather than letting Powertools for AWS Lambda (Java) generate if for you because `physicalResourceId` has a crucial role in the lifecycle of a CloudFormation custom resource. If the `physicalResourceId` changes between calls from Cloudformation, for instance in response to an `Update` event, Cloudformation [treats the resource update as a replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html). ### Customising a response diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 17391218d..5a1a0e468 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -390,7 +390,7 @@ The client was successful in receiving the result after the retry. Since the Lam 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 calculates and includes the remaining invocation available time as part of the idempotency record. +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. @@ -846,8 +846,8 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent in } ``` -!!! tip "Tip: JMESPath Powertools functions are also available" - Built-in functions like `powertools_json`, `powertools_base64`, `powertools_base64_gzip` are also available to use in this utility. See [JMESPath Powertools functions](serialization.md) +!!! tip "Tip: JMESPath Powertools for AWS Lambda (Java) functions are also available" + Built-in functions like `powertools_json`, `powertools_base64`, `powertools_base64_gzip` are also available to use in this utility. See [JMESPath Powertools for AWS Lambda (Java) functions](serialization.md) ## Testing your code diff --git a/docs/utilities/serialization.md b/docs/utilities/serialization.md index d9e392ca4..bd654393f 100644 --- a/docs/utilities/serialization.md +++ b/docs/utilities/serialization.md @@ -250,7 +250,7 @@ It can also handle a collection of elements like the records of an SQS event: ## JMESPath functions !!! Tip - [JMESPath](https://jmespath.org/){target="_blank"} is a query language for JSON used by AWS CLI and AWS Lambda Powertools for Java to get a specific part of a json. + [JMESPath](https://jmespath.org/){target="_blank"} is a query language for JSON used by AWS CLI and Powertools for AWS Lambda (Java) to get a specific part of a json. ### Key features @@ -261,11 +261,11 @@ It can also handle a collection of elements like the records of an SQS event: You might have events that contain encoded JSON payloads as string, base64, or even in compressed format. It is a common use case to decode and extract them partially or fully as part of your Lambda function invocation. -You will generally use this in combination with other Lambda Powertools modules ([validation](validation.md) and [idempotency](idempotency.md)) where you might need to extract a portion of your data before using them. +You will generally use this in combination with other Powertools for AWS Lambda (Java) modules ([validation](validation.md) and [idempotency](idempotency.md)) where you might need to extract a portion of your data before using them. ### Built-in functions -Powertools provides the following JMESPath Functions to easily deserialize common encoded JSON payloads in Lambda functions: +Powertools for AWS Lambda (Java) provides the following JMESPath Functions to easily deserialize common encoded JSON payloads in Lambda functions: #### powertools_json function diff --git a/examples/README.md b/examples/README.md index b5a0da8cd..c853937e0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,5 +1,5 @@ ## aws-lambda-powertools-examples -This directory holds example projects demoing different components of the Lambda Powertools for Java. +This directory holds example projects demoing different components of the Powertools for AWS Lambda (Java). diff --git a/examples/pom.xml b/examples/pom.xml index 546685119..95de07209 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -13,9 +13,9 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Examples</name> + <name>Powertools for AWS Lambda (Java) library Examples</name> <description> - A suite of examples accompanying for AWS Lambda Powertools. + A suite of examples accompanying for Powertools for AWS Lambda (Java). </description> <modules> diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index f4bdc399c..1101d28ad 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,6 +1,6 @@ # CoreUtilities -This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes [Lambda Powertools for operational best practices](https://github.com/awslabs/aws-lambda-powertools-java), and the following files and folders. +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes [Powertools for AWS Lambda (Java) for operational best practices](https://github.com/awslabs/aws-lambda-powertools-java), and the following files and folders. - HelloWorldFunction/src/main - Code for the application's Lambda function. - events - Invocation events that you can use to invoke the function. @@ -133,6 +133,6 @@ aws cloudformation delete-stack --stack-name <Name-of-your-deployed-stack> See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. -Check the [AWS Lambda Powertools Java](https://awslabs.github.io/aws-lambda-powertools-java/) for more information on how to use and configure such tools +Check the [Powertools for AWS Lambda (Java)](https://awslabs.github.io/aws-lambda-powertools-java/) for more information on how to use and configure such tools Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 73d874839..4bc41d35f 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - Core</name> + <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <parent> <artifactId>powertools-examples</artifactId> diff --git a/examples/powertools-examples-core/template.yaml b/examples/powertools-examples-core/template.yaml index 3ca7f0ff2..2bf3c3e98 100644 --- a/examples/powertools-examples-core/template.yaml +++ b/examples/powertools-examples-core/template.yaml @@ -12,7 +12,7 @@ Globals: Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html Environment: Variables: - # Powertools env vars: https://awslabs.github.io/aws-lambda-powertools-python/#environment-variables + # Powertools for AWS Lambda (Java) env vars: https://awslabs.github.io/aws-lambda-powertools-python/#environment-variables POWERTOOLS_LOG_LEVEL: INFO POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 POWERTOOLS_LOGGER_LOG_EVENT: true diff --git a/examples/powertools-examples-idempotency/README.md b/examples/powertools-examples-idempotency/README.md index a919f0c70..29188d89f 100644 --- a/examples/powertools-examples-idempotency/README.md +++ b/examples/powertools-examples-idempotency/README.md @@ -1,6 +1,6 @@ # Idempotency -This project contains an example of Lambda function using the idempotency module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/idempotency/). +This project contains an example of Lambda function using the idempotency module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/idempotency/). ## Deploy the sample application diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index fdd29aab5..fa577d223 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - Idempotency</name> + <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> <parent> <artifactId>powertools-examples</artifactId> diff --git a/examples/powertools-examples-parameters/README.md b/examples/powertools-examples-parameters/README.md index c03f407c0..a91dd44c9 100644 --- a/examples/powertools-examples-parameters/README.md +++ b/examples/powertools-examples-parameters/README.md @@ -1,6 +1,6 @@ # Parameters -This project contains an example of Lambda function using the parameters module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters/). +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://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters/). ## Deploy the sample application diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index d87a91c97..b588979cd 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - Parameters</name> + <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> <parent> <artifactId>powertools-examples</artifactId> diff --git a/examples/powertools-examples-serialization/README.md b/examples/powertools-examples-serialization/README.md index 68c4b4ae2..3d02f1c32 100644 --- a/examples/powertools-examples-serialization/README.md +++ b/examples/powertools-examples-serialization/README.md @@ -1,6 +1,6 @@ # Deserialization -This project contains an example of Lambda function using the serialization utilities module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/serialization/). +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://awslabs.github.io/aws-lambda-powertools-java/utilities/serialization/). ## Deploy the sample application diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 9fbdf593d..0f739e748 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - Serialization</name> + <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> <parent> <artifactId>powertools-examples</artifactId> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0c6a18fcf..608269504 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - SQS</name> + <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> <parent> <artifactId>powertools-examples</artifactId> diff --git a/examples/powertools-examples-sqs/template.yaml b/examples/powertools-examples-sqs/template.yaml index ebf20831f..5264465f9 100644 --- a/examples/powertools-examples-sqs/template.yaml +++ b/examples/powertools-examples-sqs/template.yaml @@ -11,7 +11,7 @@ Globals: Tracing: Active Environment: Variables: - # Powertools env vars: https://awslabs.github.io/aws-lambda-powertools-python/#environment-variables + # Powertools for AWS Lambda (Java) env vars: https://awslabs.github.io/aws-lambda-powertools-python/#environment-variables POWERTOOLS_LOG_LEVEL: INFO POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 POWERTOOLS_LOGGER_LOG_EVENT: true diff --git a/examples/powertools-examples-validation/README.md b/examples/powertools-examples-validation/README.md index db631deff..898e5b60e 100644 --- a/examples/powertools-examples-validation/README.md +++ b/examples/powertools-examples-validation/README.md @@ -1,6 +1,6 @@ # Validation -This project contains an example of Lambda function using the validation module of Lambda Powertools for Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/validation/). +This project contains an example of Lambda function using the validation module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/validation/). ## Deploy the sample application diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index e9a8253b4..e97ce7b56 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - Validation</name> + <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> <parent> <artifactId>powertools-examples</artifactId> diff --git a/mkdocs.yml b/mkdocs.yml index 430c0bfcc..c863b0d56 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ -site_name: AWS Lambda Powertools for Java -site_description: AWS Lambda Powertools for Java +site_name: Powertools for AWS Lambda (Java) +site_description: Powertools for AWS Lambda (Java) site_author: Amazon Web Services nav: - Homepage: index.md diff --git a/pom.xml b/pom.xml index b8703bebb..0d096141e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ <version>1.15.0</version> <packaging>pom</packaging> - <name>AWS Lambda Powertools for Java library Parent</name> + <name>Powertools for AWS Lambda (Java) library 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> @@ -48,7 +48,7 @@ <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda (Java) team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java index e721651a0..7d3a43069 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java @@ -65,7 +65,7 @@ public final Response handleRequest(CloudFormationCustomResourceEvent event, Con } catch (CustomResourceResponseException rse) { LOG.error("Unable to generate response. Sending empty failure to {}", responseUrl, rse); try { - // If the customers code throws an exception, Powertools should respond in a way that doesn't + // If the customers code throws an exception, Powertools for AWS Lambda (Java) should respond in a way that doesn't // change the CloudFormation resources. // In the case of a Update or Delete, a failure is sent with the existing PhysicalResourceId // indicating no change. 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 6dd636a73..f388f6384 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 @@ -138,7 +138,7 @@ public static Builder builder() { } /** - * Creates a failed Response with no physicalResourceId set. Powertools will set the physicalResourceId to the + * Creates a failed Response with no physicalResourceId set. Powertools for AWS Lambda (Java) will set the physicalResourceId to the * Lambda LogStreamName * * The value returned for a PhysicalResourceId can change custom resource update operations. If the value returned @@ -171,7 +171,7 @@ public static Response failed(String physicalResourceId) { } /** - * Creates a successful Response with no physicalResourceId set. Powertools will set the physicalResourceId to the + * Creates a successful Response with no physicalResourceId set. Powertools for AWS Lambda (Java) will set the physicalResourceId to the * Lambda LogStreamName * * The value returned for a PhysicalResourceId can change custom resource update operations. If the value returned diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 2efb5ff92..7a690bd0b 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Core</name> + <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> @@ -28,7 +28,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-e2e-tests/README.md b/powertools-e2e-tests/README.md index 42fe9191b..61799e6f7 100644 --- a/powertools-e2e-tests/README.md +++ b/powertools-e2e-tests/README.md @@ -1,5 +1,5 @@ ## End-to-end tests -This module is internal and meant to be used for end-to-end (E2E) testing of Lambda Powertools for Java. +This module is internal and meant to be used for end-to-end (E2E) testing of Powertools for AWS Lambda (Java). __Prerequisites__: - An AWS account is needed as well as a local environment able to reach this account diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index aa7870389..ae7ca0807 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -10,7 +10,7 @@ <artifactId>e2e-test-handler-idempotency</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools idempotency</name> + <name>A Lambda function using Powertools for AWS Lambda (Java) idempotency</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index a46702ca4..c47cc6d37 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -10,7 +10,7 @@ <artifactId>e2e-test-handler-logging</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools logging</name> + <name>A Lambda function using Powertools for AWS Lambda (Java) logging</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index b82a9a0c9..5cf30d5d5 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -7,7 +7,7 @@ <version>1.0.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> - <description>Fake handlers that use Lambda Powertools for Java.</description> + <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> <lambda.powertools.version>1.14.0</lambda.powertools.version> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index ead13344b..f3fb7059c 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -13,7 +13,7 @@ <artifactId>powertools-idempotency</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Idempotency</name> + <name>Powertools for AWS Lambda (Java) library Idempotency</name> <description> </description> @@ -27,7 +27,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index c7e910bce..205ef41d4 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -88,7 +88,7 @@ private Object processIdempotency() throws Throwable { } catch (IdempotencyKeyException ike) { throw ike; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to save in progress record to idempotency store. If you believe this is a powertools bug, please open an issue.", 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.", e); } return getFunctionResponse(); } @@ -123,7 +123,7 @@ private DataRecord getIdempotencyRecord() { } catch (IdempotencyValidationException | IdempotencyKeyException vke) { throw vke; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to get record from idempotency store. If you believe this is a powertools bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException("Failed to get record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); } } @@ -170,7 +170,7 @@ private Object getFunctionResponse() throws Throwable { } catch (IdempotencyKeyException ke) { throw ke; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to delete record from idempotency store. If you believe this is a powertools bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException("Failed to delete record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); } throw handlerException; } @@ -178,7 +178,7 @@ private Object getFunctionResponse() throws Throwable { try { persistenceStore.saveSuccess(data, response, Instant.now()); } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to update record state to success in idempotency store. If you believe this is a powertools bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException("Failed to update record state to success in idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); } return response; } diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 665030065..a044a4a34 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Logging</name> + <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> @@ -27,7 +27,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 4d6ca0eb2..a10f32629 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Metrics</name> + <name>Powertools for AWS Lambda (Java) library Metrics</name> <description> A suite of utilities for AWS Lambda Functions that make creating custom metrics via AWS Embedded Metric Format asynchronously easier. @@ -28,7 +28,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 3232d01cf..d9fcc7f50 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -12,7 +12,7 @@ <artifactId>powertools-parameters</artifactId> - <name>AWS Lambda Powertools for Java library Parameters</name> + <name>Powertools for AWS Lambda (Java) library Parameters</name> <description> Set of utilities to retrieve parameters from Secrets Manager or SSM Parameter Store @@ -27,7 +27,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> 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 index 08031e185..03584738e 100644 --- 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 @@ -16,7 +16,7 @@ /** * Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table - * is described in the Powertools documentation. + * is described in the Powertools for AWS Lambda (Java) documentation. * * @see <a href="https://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters">Parameters provider documentation</a> * diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 592cd5610..32eb53383 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -13,7 +13,7 @@ <artifactId>powertools-serialization</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Serialization Utilities</name> + <name>Powertools for AWS Lambda (Java) library Serialization Utilities</name> <description> </description> @@ -27,7 +27,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index fb5d6c276..4289a666a 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library SQS</name> + <name>Powertools for AWS Lambda (Java) library SQS</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> @@ -27,7 +27,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index d3d4bde62..24dfd646f 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -13,9 +13,9 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Test Suite</name> + <name>Powertools for AWS Lambda (Java) library Test Suite</name> <description> - A suite of tests for interactions between the various Powertools modules. + A suite of tests for interactions between the various Powertools for AWS Lambda (Java) modules. </description> <url>https://aws.amazon.com/lambda/</url> <issueManagement> @@ -27,7 +27,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 8b8dd51cb..cf1962530 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Tracing</name> + <name>Powertools for AWS Lambda (Java) library 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> @@ -28,7 +28,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> 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 ef9898a4c..cb90f3315 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 @@ -20,7 +20,7 @@ /** * {@code Tracing} is used to signal that the annotated method should - * be extended with the Powertools tracing functionality. + * be extended with the Powertools for AWS Lambda (Java) tracing functionality. * * <p>{@code Tracing} provides functionality to reduce the overhead * of performing common tracing tasks.</p> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index c60d38c68..b31f3b5bc 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java validation library</name> + <name>Powertools for AWS Lambda (Java) validation library</name> <description> Json schema validation for Lambda events and responses </description> @@ -28,7 +28,7 @@ </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> From c2baead393e91c71adb00d96d4980383a3f7facd Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <mriccia@amazon.com> Date: Fri, 2 Jun 2023 15:53:21 +0200 Subject: [PATCH 0291/1008] chore: update e2e-tests with latest Powertools version (#1173) --- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 5cf30d5d5..ca05dd290 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.14.0</lambda.powertools.version> + <lambda.powertools.version>1.15.0</lambda.powertools.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e4cb4f656..1d71159b2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.14.0</version> + <version>1.15.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> From 16c973b11f01f46c42bfac577c098db10010c895 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:13:07 +0200 Subject: [PATCH 0292/1008] build(deps): bump junit-jupiter.version from 5.9.2 to 5.9.3 (#1166) Bumps `junit-jupiter.version` from 5.9.2 to 5.9.3. --- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 4 ++-- examples/powertools-examples-serialization/pom.xml | 4 ++-- examples/powertools-examples-validation/pom.xml | 4 ++-- pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index fa577d223..76941de82 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -57,7 +57,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> <dependency> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index b588979cd..dd417f727 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -43,13 +43,13 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> </dependencies> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 0f739e748..663297dd2 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -43,13 +43,13 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> </dependencies> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index e97ce7b56..735948b33 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -38,13 +38,13 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> - <version>5.9.2</version> + <version>5.9.3</version> <scope>test</scope> </dependency> </dependencies> diff --git a/pom.xml b/pom.xml index 0d096141e..4bc9f7b0f 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version> <maven-source-plugin.version>3.2.1</maven-source-plugin.version> <maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version> - <junit-jupiter.version>5.9.2</junit-jupiter.version> + <junit-jupiter.version>5.9.3</junit-jupiter.version> <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <jmespath.version>0.5.1</jmespath.version> </properties> From 89cf3f1feb1f78f894ed08d763f89afa71e8f65d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:13:52 +0200 Subject: [PATCH 0293/1008] build(deps): bump aws.sdk.version from 2.20.29 to 2.20.67 (#1165) Bumps `aws.sdk.version` from 2.20.29 to 2.20.67. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 608269504..2bb58cb0e 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.29</version> + <version>2.20.67</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 4bc9f7b0f..4dd95fc33 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.29</aws.sdk.version> + <aws.sdk.version>2.20.67</aws.sdk.version> <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 156c0376db7f99178a494a3dfabb75f902d6c9e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:14:24 +0200 Subject: [PATCH 0294/1008] build(deps): bump maven-compiler-plugin from 3.10.1 to 3.11.0 (#1164) Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1d71159b2..0e2de4e99 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -138,7 +138,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.10.1</version> + <version>3.11.0</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From 42a346666a2996bd6dbe38a339e92479975badf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:14:54 +0200 Subject: [PATCH 0295/1008] build(deps): bump logback-classic from 1.4.6 to 1.4.7 (#1163) Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.4.6 to 1.4.7. --- pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4dd95fc33..69c17e963 100644 --- a/pom.xml +++ b/pom.xml @@ -286,7 +286,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.4.6</version> + <version>1.4.7</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0e2de4e99..dc7a30fa8 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -29,7 +29,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.4.6</version> + <version>1.4.7</version> </dependency> <dependency> From cf2dd63c2c953e201729b0b31bff7eba638062a0 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Mon, 5 Jun 2023 13:07:52 +0200 Subject: [PATCH 0296/1008] fix(docs): add site_url to docs --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index c863b0d56..613402914 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,7 @@ 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/ nav: - Homepage: index.md - Changelog: changelog.md From db32719a97702857f46e7002937803b20b73c619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:36:32 +0200 Subject: [PATCH 0297/1008] E2E Tests on Java 17 (#1174) --- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- powertools-e2e-tests/handlers/logging/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 4 ++-- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 4 ++-- .../amazon/lambda/powertools/testutils/Infrastructure.java | 2 ++ .../amazon/lambda/powertools/testutils/JavaRuntime.java | 3 ++- 8 files changed, 12 insertions(+), 9 deletions(-) diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index ae7ca0807..4e24c738c 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -27,7 +27,7 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <configuration> <source>${maven.compiler.source}</source> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index c47cc6d37..4b613f2bf 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -27,7 +27,7 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <configuration> <source>${maven.compiler.source}</source> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index e591f4966..723ad75c5 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -27,7 +27,7 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <configuration> <source>${maven.compiler.source}</source> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ca05dd290..5e7a9caa2 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -99,9 +99,9 @@ </dependencies> </plugin> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>${aspectj.version}</version> + <version>1.13.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 831669a3d..252009aa9 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -27,7 +27,7 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <configuration> <source>${maven.compiler.source}</source> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index dc7a30fa8..7fa2a40a6 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,8 +17,8 @@ <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> - <constructs.version>10.2.25</constructs.version> - <cdk.version>2.47.0</cdk.version> + <constructs.version>10.2.30</constructs.version> + <cdk.version>2.79.1</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> 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 f3659e5c7..1fdbd3836 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 @@ -162,6 +162,8 @@ private void getJavaRuntime() { runtime = JavaRuntime.JAVA8AL2; } else if (javaVersion.startsWith("11")) { runtime = JavaRuntime.JAVA11; + } else if (javaVersion.startsWith("17")) { + runtime = JavaRuntime.JAVA17; } else { throw new IllegalArgumentException("Unsupported Java version " + javaVersion); } 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 94ec13518..dce97538f 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 @@ -5,7 +5,8 @@ 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"); + JAVA11("java11", Runtime.JAVA_11, "11"), + JAVA17("java17", Runtime.JAVA_17, "17"); private final String runtime; private final Runtime cdkRuntime; From 0fd2ecf11a5a0133c104f38ef3bc09d5840fcb14 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <mriccia@amazon.com> Date: Mon, 5 Jun 2023 15:14:48 +0200 Subject: [PATCH 0298/1008] chore: Swap implementation of `aspectj-maven-plugin` to support Java 17 (#1172) --- README.md | 4 ++-- docs/index.md | 6 ++++-- docs/utilities/batch.md | 6 ++++-- docs/utilities/idempotency.md | 4 ++-- docs/utilities/parameters.md | 7 +++++-- docs/utilities/sqs_large_message_handling.md | 6 ++++-- docs/utilities/validation.md | 6 ++++-- examples/powertools-examples-core/pom.xml | 4 ++-- examples/powertools-examples-idempotency/pom.xml | 4 ++-- examples/powertools-examples-parameters/pom.xml | 4 ++-- examples/powertools-examples-sqs/pom.xml | 4 ++-- examples/powertools-examples-validation/pom.xml | 4 ++-- pom.xml | 6 +++--- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-test-suite/pom.xml | 4 ++-- 16 files changed, 42 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index b13e05f6c..04a0ad288 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambd <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>1.8</source> <target>1.8</target> diff --git a/docs/index.md b/docs/index.md index f595c1b65..10726f0ab 100644 --- a/docs/index.md +++ b/docs/index.md @@ -71,9 +71,9 @@ For more information about the project and available options refer to this [repo <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>1.8</source> <target>1.8</target> @@ -122,6 +122,8 @@ For more information about the project and available options refer to this [repo aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' +// This dependency is needed for Java17+, please uncomment it if you are using Java17+ +// implementation 'org.aspectj:aspectjrt:1.9.19' } sourceCompatibility = 11 diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 1c3586360..0a2add081 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -41,9 +41,9 @@ To install this utility, add the following dependency to your project. <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>1.8</source> <target>1.8</target> @@ -83,6 +83,8 @@ To install this utility, add the following dependency to your project. dependencies { ... aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' +// This dependency is needed for Java17+, please uncomment it if you are using Java17+ +// implementation 'org.aspectj:aspectjrt:1.9.19' } ``` diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 5a1a0e468..d8ec13e9d 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -43,9 +43,9 @@ times with the same parameters**. This makes idempotent operations safe to retry <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>1.8</source> <target>1.8</target> diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index e8df1f8d6..9041ac08e 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -34,6 +34,8 @@ To install this utility, add the following dependency to your project. dependencies { ... aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' +// This dependency is needed for Java17+, please uncomment it if you are using Java17+ +// implementation 'org.aspectj:aspectjrt:1.9.19' } ``` @@ -441,9 +443,9 @@ If you want to use the ```@Param``` annotation in your project add configuration <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> ... <aspectLibraries> @@ -482,5 +484,6 @@ If you want to use the ```@Param``` annotation in your project add configuration dependencies { ... aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' + implementation 'org.aspectj:aspectjrt:1.9.19' } ``` \ No newline at end of file diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md index 65c4b7b4d..5f7ede095 100644 --- a/docs/utilities/sqs_large_message_handling.md +++ b/docs/utilities/sqs_large_message_handling.md @@ -49,9 +49,9 @@ To install this utility, add the following dependency to your project. <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>1.8</source> <target>1.8</target> @@ -91,6 +91,8 @@ To install this utility, add the following dependency to your project. dependencies { ... aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' +// This dependency is needed for Java17+, please uncomment it if you are using Java17+ +// implementation 'org.aspectj:aspectjrt:1.9.19' } ``` diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index c51f905aa..8733c0e77 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -31,9 +31,9 @@ To install this utility, add the following dependency to your project. <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>1.8</source> <target>1.8</target> @@ -72,6 +72,8 @@ To install this utility, add the following dependency to your project. dependencies { aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' +// This dependency is needed for Java17+, please uncomment it if you are using Java17+ +// implementation 'org.aspectj:aspectjrt:1.9.19' } ``` diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 4bc41d35f..b66b2cdbd 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -59,9 +59,9 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 76941de82..38f5787e9 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -76,9 +76,9 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index dd417f727..9e9ffe1b0 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -63,9 +63,9 @@ <version>3.1.0</version> </plugin> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 2bb58cb0e..be4376241 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -64,9 +64,9 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 735948b33..2f42ec370 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -58,9 +58,9 @@ <version>3.1.0</version> </plugin> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/pom.xml b/pom.xml index 69c17e963..bb277f32e 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ <lambda.events.version>3.11.1</lambda.events.version> <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> - <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version> + <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version> @@ -307,7 +307,7 @@ <version>${maven-compiler-plugin.version}</version> </plugin> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>${aspectj-maven-plugin.version}</version> </plugin> @@ -354,7 +354,7 @@ </configuration> </plugin> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>${aspectj-maven-plugin.version}</version> <configuration> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 5e7a9caa2..be53a9a5b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -18,7 +18,7 @@ <lambda.java.core>1.2.2</lambda.java.core> <lambda.java.events>3.11.0</lambda.java.events> <maven.shade.version>3.2.4</maven.shade.version> - <aspectj.version>1.14.0</aspectj.version> + <aspectj.version>1.13.1</aspectj.version> <maven.compiler.version>3.10.1</maven.compiler.version> </properties> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 32eb53383..e68b26f7b 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -75,7 +75,7 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>${aspectj-maven-plugin.version}</version> <configuration> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 24dfd646f..2cd323f41 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -115,9 +115,9 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From 6ce53592bb0da61823ec34f808b43d3e6c7216c9 Mon Sep 17 00:00:00 2001 From: Mark Sailes <45629314+msailes@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:15:28 +0200 Subject: [PATCH 0299/1008] fix:Removing env var credentials provider as default. (#1161) SnapStart uses a different credentials provider so when it is hardcoded like this, the default will fail. --- .../core/internal/LambdaConstants.java | 21 ++++++++++ .../powertools/idempotency/Constants.java | 2 - .../persistence/BasePersistenceStore.java | 5 ++- .../persistence/DynamoDBPersistenceStore.java | 17 ++++++-- powertools-parameters/pom.xml | 4 ++ .../powertools/parameters/SSMProvider.java | 39 ++++++++++--------- .../parameters/SecretsProvider.java | 38 +++++++++--------- 7 files changed, 83 insertions(+), 43 deletions(-) create mode 100644 powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java new file mode 100644 index 000000000..fe30b4928 --- /dev/null +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java @@ -0,0 +1,21 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +public class LambdaConstants { + public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME"; + public static final String AWS_REGION_ENV = "AWS_REGION"; + public static final String AWS_LAMBDA_INITIALIZATION_TYPE = "AWS_LAMBDA_INITIALIZATION_TYPE"; + public static final String ON_DEMAND = "on-demand"; +} diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java index d8f7a9a13..28c6f58aa 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java @@ -14,7 +14,5 @@ package software.amazon.lambda.powertools.idempotency; public class Constants { - public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME"; - public static final String AWS_REGION_ENV = "AWS_REGION"; public static final String IDEMPOTENCY_DISABLED_ENV = "POWERTOOLS_IDEMPOTENCY_DISABLED"; } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index 79db29f05..727405257 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -20,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.utils.StringUtils; -import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; @@ -43,6 +42,8 @@ import java.util.Spliterator; import java.util.stream.StreamSupport; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; + /** * Persistence layer that will store the idempotency result. * Base implementation. See {@link DynamoDBPersistenceStore} for an implementation (default one) @@ -71,7 +72,7 @@ public abstract class BasePersistenceStore implements PersistenceStore { * @param functionName The name of the function being decorated */ public void configure(IdempotencyConfig config, String functionName) { - String funcEnv = System.getenv(Constants.LAMBDA_FUNCTION_NAME_ENV); + String funcEnv = System.getenv(LAMBDA_FUNCTION_NAME_ENV); this.functionName = funcEnv != null ? funcEnv : "testFunction"; if (!StringUtils.isEmpty(functionName)) { this.functionName += "." + functionName; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index e48cb78e2..47ddf4c5c 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -34,7 +34,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static software.amazon.lambda.powertools.idempotency.Constants.AWS_REGION_ENV; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; +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.core.internal.LambdaConstants.ON_DEMAND; import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; /** @@ -86,9 +89,17 @@ private DynamoDBPersistenceStore(String tableName, String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); if (idempotencyDisabledEnv == null || idempotencyDisabledEnv.equalsIgnoreCase("false")) { DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder() - .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) .httpClient(UrlConnectionHttpClient.builder().build()) .region(Region.of(System.getenv(AWS_REGION_ENV))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(ON_DEMAND)) { + ddbBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + this.dynamoDbClient = ddbBuilder.build(); } else { // we do not want to create a DynamoDbClient if idempotency is disabled @@ -249,7 +260,7 @@ public static Builder builder() { * You can also set a custom {@link DynamoDbClient} for further tuning. */ public static class Builder { - private static final String funcEnv = System.getenv(Constants.LAMBDA_FUNCTION_NAME_ENV); + private static final String funcEnv = System.getenv(LAMBDA_FUNCTION_NAME_ENV); private String tableName; private String keyAttr = "id"; diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index d9fcc7f50..3df5a9b0c 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -41,6 +41,10 @@ </distributionManagement> <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-core</artifactId> + </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>ssm</artifactId> diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index a74e1c095..bf36aa717 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -22,14 +22,18 @@ 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.SsmClientBuilder; 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.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; + /** * AWS System Manager Parameter Store Provider <br/><br/> * @@ -75,20 +79,6 @@ public class SSMProvider extends BaseProvider { private boolean decrypt = false; private boolean recursive = false; - /** - * Default constructor with default {@link SsmClient}. <br/> - * Use when you don't 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. - */ - SSMProvider(CacheManager cacheManager) { - this(cacheManager, SsmClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build()); - } - /** * Constructor with custom {@link SsmClient}. <br/> * Use when you need to customize region or any other attribute of the client.<br/><br/> @@ -253,11 +243,24 @@ public SSMProvider build() { throw new IllegalStateException("No CacheManager provided, please provide one"); } SSMProvider provider; - if (client != null) { - provider = new SSMProvider(cacheManager, client); - } else { - provider = new SSMProvider(cacheManager); + if (client == null) { + SsmClientBuilder ssmClientBuilder = SsmClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { + ssmClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + + client = ssmClientBuilder.build(); } + + provider = new SSMProvider(cacheManager, client); + if (transformationManager != null) { provider.setTransformationManager(transformationManager); } 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 index a0a8fe51e..9764564a9 100644 --- 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 @@ -22,12 +22,15 @@ 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.SecretsManagerClientBuilder; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.lambda.powertools.core.internal.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; /** * AWS Secrets Manager Parameter Provider<br/><br/> @@ -57,20 +60,6 @@ public class SecretsProvider extends BaseProvider { private final SecretsManagerClient client; - /** - * Default constructor with default {@link SecretsManagerClient}. <br/> - * Use when you don't need to customize region or any other attribute of the client.<br/><br/> - * - * Use the {@link Builder} to create an instance of it. - */ - SecretsProvider(CacheManager cacheManager) { - this(cacheManager, SecretsManagerClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build()); - } - /** * Constructor with custom {@link SecretsManagerClient}. <br/> * Use when you need to customize region or any other attribute of the client.<br/><br/> @@ -162,11 +151,24 @@ public SecretsProvider build() { throw new IllegalStateException("No CacheManager provided, please provide one"); } SecretsProvider provider; - if (client != null) { - provider = new SecretsProvider(cacheManager, client); - } else { - provider = new SecretsProvider(cacheManager); + if (client == null) { + SecretsManagerClientBuilder secretsManagerClientBuilder = SecretsManagerClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { + secretsManagerClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + + client = secretsManagerClientBuilder.build(); } + + provider = new SecretsProvider(cacheManager, client); + if (transformationManager != null) { provider.setTransformationManager(transformationManager); } From b56963e543d6491d3aa7af96e7496ae99f616075 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 14:42:11 +0200 Subject: [PATCH 0300/1008] build(deps-dev): bump constructs from 10.2.30 to 10.2.47 (#1185) Bumps [constructs](https://github.com/aws/constructs) from 10.2.30 to 10.2.47. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 7fa2a40a6..d9b271c06 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> - <constructs.version>10.2.30</constructs.version> + <constructs.version>10.2.47</constructs.version> <cdk.version>2.79.1</cdk.version> <!-- Don't deploy the e2e tests --> From ed92636563bb9544408053faea17d40b5cca31ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 14:42:40 +0200 Subject: [PATCH 0301/1008] build(deps): bump maven-source-plugin from 3.2.1 to 3.3.0 (#1179) Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.1 to 3.3.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb277f32e..fee73188f 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version> - <maven-source-plugin.version>3.2.1</maven-source-plugin.version> + <maven-source-plugin.version>3.3.0</maven-source-plugin.version> <maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version> <junit-jupiter.version>5.9.3</junit-jupiter.version> <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> From 76ca20f2df7631b6cf4eaed852a55ab95f27be1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 14:43:10 +0200 Subject: [PATCH 0302/1008] build(deps): bump aws.xray.recorder.version from 2.13.0 to 2.14.0 (#1178) Bumps `aws.xray.recorder.version` from 2.13.0 to 2.14.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fee73188f..dfa923316 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> <aws.sdk.version>2.20.67</aws.sdk.version> - <aws.xray.recorder.version>2.13.0</aws.xray.recorder.version> + <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.2</lambda.core.version> From 61728a81f92d27a4092bd6b56ca839031f48499d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 08:58:49 +0200 Subject: [PATCH 0303/1008] build(deps): bump DynamoDBLocal from 1.21.0 to 1.22.0 (#1189) Bumps DynamoDBLocal from 1.21.0 to 1.22.0. --- examples/powertools-examples-idempotency/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 38f5787e9..acfa0bb47 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -63,7 +63,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>DynamoDBLocal</artifactId> - <version>1.21.0</version> + <version>1.22.0</version> <scope>test</scope> </dependency> <dependency> From 96271067b61927063ef94669493418372e5bd3ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 09:08:49 +0200 Subject: [PATCH 0304/1008] build(deps-dev): bump aws-cdk-lib from 2.79.1 to 2.83.1 (#1188) Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.79.1 to 2.83.1. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index d9b271c06..77896adb7 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -18,7 +18,7 @@ <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <constructs.version>10.2.47</constructs.version> - <cdk.version>2.79.1</cdk.version> + <cdk.version>2.83.1</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From 24ca88309125e10d20d9c77c6722b0f42bb52fa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 09:19:49 +0200 Subject: [PATCH 0305/1008] build(deps): bump json-schema-validator from 1.0.78 to 1.0.84 (#1187) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.78 to 1.0.84. --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index b31f3b5bc..fbd076f0a 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.78</version> + <version>1.0.84</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 54aefe7794a7970ed7d0933c68e40fd1cd0277f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 09:20:18 +0200 Subject: [PATCH 0306/1008] build(deps): bump aws.sdk.version from 2.20.67 to 2.20.82 (#1186) Bumps `aws.sdk.version` from 2.20.67 to 2.20.82. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index be4376241..85000cdba 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.67</version> + <version>2.20.82</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index dfa923316..18c9f1416 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.67</aws.sdk.version> + <aws.sdk.version>2.20.82</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 94463be42e5192c8bedcc6ca967bb8b862be63c5 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden <jeromevdl@gmail.com> Date: Wed, 14 Jun 2023 16:32:17 +0200 Subject: [PATCH 0307/1008] auto merge dependabot PR --- .github/workflows/auto-merge.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 18b4d7328..17318dad1 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -5,6 +5,12 @@ on: workflows: ["Build"] types: [completed] +permissions: + pull-requests: write + issues: write + repository-projects: write + contents: write + jobs: merge-me: name: Merge me! @@ -51,10 +57,13 @@ jobs: }) github-token: ${{ secrets.AUTOMERGE }} - - name: Merge me! - uses: peter-evans/create-or-update-comment@v2 # use dependabot comments to automatically merge + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1.1.1 with: - issue-number: ${{ github.event.pull_request.number }} - body: | - dependabot squash and merge - reactions: rocket + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} From 4993e2147ee4b8df62b84064eb2351f4d42048c2 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden <jeromevdl@gmail.com> Date: Wed, 14 Jun 2023 17:38:40 +0200 Subject: [PATCH 0308/1008] auto merge dependabot PR --- .github/workflows/auto-merge.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 17318dad1..21b5e20e5 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -55,15 +55,12 @@ jobs: 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 }} - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v1.1.1 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Enable auto-merge for Dependabot PRs - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} From 3bb02728150bfc48610731f5172f0c4ef12751bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 17:49:17 +0200 Subject: [PATCH 0309/1008] build(deps): bump aws.sdk.version from 2.20.82 to 2.20.85 (#1192) Bumps `aws.sdk.version` from 2.20.82 to 2.20.85. Updates `software.amazon.awssdk:bom` from 2.20.82 to 2.20.85 Updates `http-client-spi` from 2.20.82 to 2.20.85 Updates `url-connection-client` from 2.20.82 to 2.20.85 Updates `s3` from 2.20.82 to 2.20.85 Updates `lambda` from 2.20.82 to 2.20.85 Updates `cloudwatch` from 2.20.82 to 2.20.85 Updates `xray` from 2.20.82 to 2.20.85 Updates `cloudformation` from 2.20.82 to 2.20.85 Updates `sts` from 2.20.82 to 2.20.85 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 85000cdba..502b8d46a 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.82</version> + <version>2.20.85</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 18c9f1416..4b7f49eb4 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.82</aws.sdk.version> + <aws.sdk.version>2.20.85</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 16953c31d0d221c73aec1dc3340e919668b423fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:01:42 +0200 Subject: [PATCH 0310/1008] build(deps): bump spotbugs-maven-plugin from 4.7.3.2 to 4.7.3.4 (#1193) Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.7.3.2 to 4.7.3.4. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.7.3.2...spotbugs-maven-plugin-4.7.3.4) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b7f49eb4..751b3fd2d 100644 --- a/pom.xml +++ b/pom.xml @@ -454,7 +454,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.2</version> + <version>4.7.3.4</version> <executions> <execution> <id>test</id> From a72fb3db8ebe4e73cc559b3f9e9e6bb4197b0099 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:02:16 +0200 Subject: [PATCH 0311/1008] build(deps): bump jackson-datatype-joda from 2.14.2 to 2.15.2 (#1195) Bumps [jackson-datatype-joda](https://github.com/FasterXML/jackson-datatype-joda) from 2.14.2 to 2.15.2. - [Commits](https://github.com/FasterXML/jackson-datatype-joda/compare/jackson-datatype-joda-2.14.2...jackson-datatype-joda-2.15.2) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-joda dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 502b8d46a..cf13858f9 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -50,7 +50,7 @@ <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-joda</artifactId> - <version>2.14.2</version> + <version>2.15.2</version> </dependency> <dependency> From 9d159d1299ca67c74a877f60754056abff783667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:34:06 +0200 Subject: [PATCH 0312/1008] build(deps-dev): bump aws-cdk-lib from 2.83.1 to 2.84.0 (#1198) Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.83.1 to 2.84.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.83.1...v2.84.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 77896adb7..96c68b5b6 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -18,7 +18,7 @@ <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <constructs.version>10.2.47</constructs.version> - <cdk.version>2.83.1</cdk.version> + <cdk.version>2.84.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From 5737e60e164b178fd12c0be5360d7c775dd8f37b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:35:03 +0200 Subject: [PATCH 0313/1008] build(deps): bump maven-failsafe-plugin from 3.1.0 to 3.1.2 (#1199) Bumps [maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.0...surefire-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 96c68b5b6..28f1be779 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -156,7 +156,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.1.0</version> + <version>3.1.2</version> <executions> <execution> <goals> From c96c0ad6be7776951f6afdfda3ad70c9b20992b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:35:47 +0200 Subject: [PATCH 0314/1008] build(deps): bump maven-gpg-plugin from 3.0.1 to 3.1.0 (#1197) Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.0.1 to 3.1.0. - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.0.1...maven-gpg-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 751b3fd2d..748588d7e 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <nexus-staging-maven-plugin.version>1.6.8</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.0.1</maven-gpg-plugin.version> + <maven-gpg-plugin.version>3.1.0</maven-gpg-plugin.version> <junit-jupiter.version>5.9.3</junit-jupiter.version> <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <jmespath.version>0.5.1</jmespath.version> From bcb99bac8c21b14f147721a363f0452067c49156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:34:07 +0200 Subject: [PATCH 0315/1008] fix: missing idempotency key should not persist any data (#1201) --- docs/utilities/idempotency.md | 4 +- .../internal/IdempotencyHandler.java | 6 ++- .../persistence/BasePersistenceStore.java | 43 ++++++++++++++----- .../persistence/BasePersistenceStoreTest.java | 6 +-- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index d8ec13e9d..c8dd59000 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -708,10 +708,12 @@ By using **`withPayloadValidationJMESPath("amount")`**, we prevent this potentia ### Making idempotency key required -If you want to enforce that an idempotency key is required, you can set **`ThrowOnNoIdempotencyKey`** to `True`. +If you want to enforce that an idempotency key is required, you can set **`ThrowOnNoIdempotencyKey`** to `true`. This means that we will throw **`IdempotencyKeyException`** if the evaluation of **`EventKeyJMESPath`** is `null`. +When set to `false` (the default), if the idempotency key is null, then the data is not persisted in the store. + === "App.java" ```java hl_lines="9-10 13" diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 205ef41d4..5ce723f04 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -84,7 +84,9 @@ private Object processIdempotency() throws Throwable { persistenceStore.saveInProgress(data, Instant.now(), getRemainingTimeInMillis()); } catch (IdempotencyItemAlreadyExistsException iaee) { DataRecord record = getIdempotencyRecord(); - return handleForStatus(record); + if (record != null) { + return handleForStatus(record); + } } catch (IdempotencyKeyException ike) { throw ike; } catch (Exception e) { @@ -111,7 +113,7 @@ private OptionalInt getRemainingTimeInMillis() { /** * Retrieve the idempotency record from the persistence layer. * - * @return the record if available + * @return the record if available, potentially null */ private DataRecord getIdempotencyRecord() { try { diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index 727405257..c79068d1a 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -35,11 +35,12 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; -import java.util.OptionalLong; +import java.util.Optional; import java.util.OptionalInt; -import java.util.stream.Stream; -import java.util.Spliterators; +import java.util.OptionalLong; import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; import java.util.stream.StreamSupport; import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; @@ -117,8 +118,13 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { } else { responseJson = writer.writeValueAsString(result); } + Optional<String> hashedIdempotencyKey = getHashedIdempotencyKey(data); + if (!hashedIdempotencyKey.isPresent()) { + // missing idempotency key => non-idempotent transaction, we do not store the data, simply return + return; + } DataRecord record = new DataRecord( - getHashedIdempotencyKey(data), + hashedIdempotencyKey.get(), DataRecord.Status.COMPLETED, getExpiryEpochSecond(now), responseJson, @@ -140,8 +146,13 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { * @param now */ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTimeInMs) throws IdempotencyItemAlreadyExistsException { - String idempotencyKey = getHashedIdempotencyKey(data); + Optional<String> hashedIdempotencyKey = getHashedIdempotencyKey(data); + if (!hashedIdempotencyKey.isPresent()) { + // missing idempotency key => non-idempotent transaction, we do not store the data, simply return + return; + } + String idempotencyKey = hashedIdempotencyKey.get(); if (retrieveFromCache(idempotencyKey, now) != null) { throw new IdempotencyItemAlreadyExistsException(); } @@ -170,8 +181,13 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime * @param throwable The throwable thrown by the function */ public void deleteRecord(JsonNode data, Throwable throwable) { - String idemPotencyKey = getHashedIdempotencyKey(data); + Optional<String> hashedIdempotencyKey = getHashedIdempotencyKey(data); + if (!hashedIdempotencyKey.isPresent()) { + // missing idempotency key => non-idempotent transaction, we do not delete the data, simply return + return; + } + String idemPotencyKey = hashedIdempotencyKey.get(); LOG.debug("Function raised an exception {}. " + "Clearing in progress record in persistence store for idempotency key: {}", throwable.getClass(), @@ -190,8 +206,13 @@ public void deleteRecord(JsonNode data, Throwable throwable) { * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key */ public DataRecord getRecord(JsonNode data, Instant now) throws IdempotencyValidationException, IdempotencyItemNotFoundException { - String idemPotencyKey = getHashedIdempotencyKey(data); + Optional<String> hashedIdempotencyKey = getHashedIdempotencyKey(data); + if (!hashedIdempotencyKey.isPresent()) { + // missing idempotency key => non-idempotent transaction, we do not get the data, simply return nothing + return null; + } + String idemPotencyKey = hashedIdempotencyKey.get(); DataRecord cachedRecord = retrieveFromCache(idemPotencyKey, now); if (cachedRecord != null) { LOG.debug("Idempotency record found in cache with idempotency key: {}", idemPotencyKey); @@ -211,7 +232,7 @@ public DataRecord getRecord(JsonNode data, Instant now) throws IdempotencyValida * @param data incoming data * @return Hashed representation of the data extracted by the jmespath expression */ - private String getHashedIdempotencyKey(JsonNode data) { + private Optional<String> getHashedIdempotencyKey(JsonNode data) { JsonNode node = data; if (eventKeyJMESPath != null) { @@ -221,13 +242,15 @@ private String getHashedIdempotencyKey(JsonNode data) { if (isMissingIdemPotencyKey(node)) { if (throwOnNoIdempotencyKey) { throw new IdempotencyKeyException("No data found to create a hashed idempotency key"); + } else { + LOG.warn("No data found to create a hashed idempotency key. JMESPath: {}", eventKeyJMESPath); + return Optional.empty(); } - LOG.warn("No data found to create a hashed idempotency key. JMESPath: {}", eventKeyJMESPath); } String hash = generateHash(node); hash = functionName + "#" + hash; - return hash; + return Optional.of(hash); } private boolean isMissingIdemPotencyKey(JsonNode data) { diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index e77a995a4..47e5df1ab 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -143,15 +143,15 @@ public void saveInProgress_jmespath_NotFound_shouldThrowException() { } @Test - public void saveInProgress_jmespath_NotFound_shouldNotThrowException() { + public void saveInProgress_jmespath_NotFound_shouldNotPersist() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder() .withEventKeyJMESPath("unavailable") .build(), ""); Instant now = Instant.now(); persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); - assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); - assertThat(status).isEqualTo(1); + assertThat(dr).isNull(); + assertThat(status).isEqualTo(-1); } @Test From a568eac9ad72c258add2a2aa91c8187cba74d6b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:06:03 +0200 Subject: [PATCH 0316/1008] build(deps): bump aws.sdk.version from 2.20.85 to 2.20.86 (#1202) Bumps `aws.sdk.version` from 2.20.85 to 2.20.86. Updates `software.amazon.awssdk:bom` from 2.20.85 to 2.20.86 Updates `http-client-spi` from 2.20.85 to 2.20.86 Updates `url-connection-client` from 2.20.85 to 2.20.86 Updates `s3` from 2.20.85 to 2.20.86 Updates `lambda` from 2.20.85 to 2.20.86 Updates `cloudwatch` from 2.20.85 to 2.20.86 Updates `xray` from 2.20.85 to 2.20.86 Updates `cloudformation` from 2.20.85 to 2.20.86 Updates `sts` from 2.20.85 to 2.20.86 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index cf13858f9..155b107f4 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.85</version> + <version>2.20.86</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 748588d7e..f7846b1b5 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <log4j.version>2.20.0</log4j.version> <jackson.version>2.14.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> - <aws.sdk.version>2.20.85</aws.sdk.version> + <aws.sdk.version>2.20.86</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 01bc50ba9afe4a38b76f4d4a5f6b664626805c0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:06:20 +0200 Subject: [PATCH 0317/1008] build(deps): bump maven-shade-plugin from 3.4.1 to 3.5.0 (#1204) Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.1...maven-shade-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index b66b2cdbd..7338ba665 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -92,7 +92,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.4.1</version> + <version>3.5.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index acfa0bb47..130eadd6a 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -142,7 +142,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.4.1</version> + <version>3.5.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 155b107f4..83588291a 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -93,7 +93,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.4.1</version> + <version>3.5.0</version> <executions> <execution> <phase>package</phase> From a273639d5e1a2d8ba119e114a45327b0209bc185 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:23:43 +0200 Subject: [PATCH 0318/1008] chore: Change repo URL to the new location (#1171) Updates repo to the new org --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/ISSUE_TEMPLATE/maintenance.yml | 2 +- .github/ISSUE_TEMPLATE/rfc.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/release-prep.yml | 2 +- CHANGELOG.md | 52 +++++++++---------- README.md | 4 +- docs/core/logging.md | 2 +- docs/index.md | 2 +- docs/utilities/idempotency.md | 2 +- examples/powertools-examples-core/README.md | 10 ++-- .../powertools-examples-idempotency/README.md | 2 +- .../powertools-examples-parameters/README.md | 2 +- .../README.md | 2 +- .../powertools-examples-validation/README.md | 2 +- mkdocs.yml | 2 +- pom.xml | 4 +- powertools-cloudformation/pom.xml | 8 +-- powertools-core/pom.xml | 4 +- powertools-e2e-tests/pom.xml | 4 +- powertools-idempotency/README.md | 2 +- powertools-idempotency/pom.xml | 4 +- powertools-logging/pom.xml | 4 +- powertools-metrics/pom.xml | 4 +- powertools-parameters/pom.xml | 4 +- .../parameters/DynamoDbProvider.java | 2 +- .../powertools/parameters/ParamManager.java | 2 +- powertools-serialization/pom.xml | 4 +- powertools-sqs/pom.xml | 4 +- .../lambda/powertools/sqs/SqsUtils.java | 2 +- powertools-test-suite/pom.xml | 4 +- powertools-tracing/pom.xml | 4 +- powertools-validation/pom.xml | 4 +- 34 files changed, 79 insertions(+), 79 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 42170fa86..abd8faa56 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -39,7 +39,7 @@ assignees: '' * **AWS Lambda function runtime:** * **Debugging logs** -> [How to enable debug mode](https://awslabs.github.io/aws-lambda-powertools-java/#debug-mode)** +> [How to enable debug mode](https://docs.powertools.aws.dev/lambda-java/#debug-mode)** ```text # paste logs here diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 100078541..9eae2e167 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Ask a question - url: https://github.com/awslabs/aws-lambda-powertools-java/discussions/new + url: https://github.com/aws-powertools/powertools-lambda-java/discussions/new about: Ask a general question about Lambda Powertools diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml index 990c84507..7a8b50c9a 100644 --- a/.github/ISSUE_TEMPLATE/maintenance.yml +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -55,7 +55,7 @@ body: attributes: label: Acknowledgment options: - - label: This request meets [Powertools for AWS Lambda (Java) Tenets](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) + - 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/awslabs/aws-lambda-powertools-python/), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript/) required: false diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md index 5a1205884..84fce71df 100644 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ b/.github/ISSUE_TEMPLATE/rfc.md @@ -12,7 +12,7 @@ assignees: '' * RFC PR: (leave this empty) * Related issue(s), if known: * Area: (i.e. Tracer, Metrics, Logger, etc.) -* Meet [tenets](https://awslabs.github.io/aws-lambda-powertools-java/#tenets): (Yes/no) +* Meet [tenets](https://docs.powertools.aws.dev/lambda-java/#tenets): (Yes/no) ## Summary [summary]: #summary diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index db7e42bfc..d4b6d17ff 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,7 +8,7 @@ <!--- Leave unchecked if your change doesn't seem to apply --> -* [ ] [Meet tenets criteria](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) +* [ ] [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/) diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml index b2cf656ba..345bd2a10 100644 --- a/.github/workflows/release-prep.yml +++ b/.github/workflows/release-prep.yml @@ -75,6 +75,6 @@ jobs: 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/awslabs/aws-lambda-powertools-java/blob/prep-release-${{ github.event.inputs.targetRelease }}/CHANGELOG.md) to capture changes in this release. Please review changes carefully before merging. + 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/CHANGELOG.md b/CHANGELOG.md index 9e9e6ceb7..fda9bc732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### 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://awslabs.github.io/aws-lambda-powertools-java/utilities/custom_resources/). (#1082) by @msailes +* 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 ### Maintenance @@ -66,22 +66,22 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo #### Maintenance -* Fixes to resolve vulnerable transitive dependencies ([919](https://github.com/awslabs/aws-lambda-powertools-java/issues/919)) +* Fixes to resolve vulnerable transitive dependencies ([919](https://github.com/aws-powertools/powertools-lambda-java/issues/919)) ## [1.12.2] - 2022-04-29 ### Bug Fixes -* **SQS Large message processing**: Classpath conflict on `PayloadS3Pointer` when consumer application depends on `payloadoffloading-common`, introduced in [v1.8.0](https://github.com/awslabs/aws-lambda-powertools-java/releases/tag/v1.8.0). ([#851](https://github.com/awslabs/aws-lambda-powertools-java/pull/851)) +* **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)) ## [1.12.1] - 2022-04-21 ### Bug Fixes -* **Idempotency**: thread-safety issue of MessageDigest ([#817](https://github.com/awslabs/aws-lambda-powertools-java/pull/817)) -* **Idempotency**: disable dynamodb client creation in persistent store when disabling idempotency ([#796](https://github.com/awslabs/aws-lambda-powertools-java/pull/796)) +* **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)) ### Maintenance @@ -92,10 +92,10 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [1.12.0] - 2022-03-01 ### Added - * **Easy Event Deserialization**: Extraction and deserialization of the main content of events (body, messages, ...) [#757](https://github.com/awslabs/aws-lambda-powertools-java/pull/757) + * **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) ### Bug Fixes - * Different behavior while using SSMProvider with or without trailing slash in parameter names [#758](https://github.com/awslabs/aws-lambda-powertools-java/issues/758) + * Different behavior while using SSMProvider with or without trailing slash in parameter names [#758](https://github.com/aws-powertools/powertools-lambda-java/issues/758) ## [1.11.0] - 2022-02-16 @@ -109,17 +109,17 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### Bug Fixes -* **SQS Batch processing**: Prevent message to be marked as success if failed sending to DLQ for non retryable exceptions. [#731](https://github.com/awslabs/aws-lambda-powertools-java/pull/731) +* **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) ### Documentation -* **SQS Batch processing**: Improve [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/batch/#iam-permissions) on IAM premissions required by function when using utility with an encrypted SQS queue with customer managed KMS keys. +* **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. ## [1.10.2] - 2022-01-07 -* **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/awslabs/aws-lambda-powertools-java/pull/698) +* **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) ## [1.10.1] - 2022-01-06 @@ -127,13 +127,13 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [1.10.0] - 2021-12-27 -* **Logging**: Modern log4j configuration to customise structured logging. Refer [docs](https://awslabs.github.io/aws-lambda-powertools-java/core/logging/#upgrade-to-jsontemplatelayout-from-deprecated-lambdajsonlayout-configuration-in-log4j2xml) to start using new config. [#670](https://github.com/awslabs/aws-lambda-powertools-java/pull/670) -* **SQS Batch**: Support batch size greater than 10. [#667](https://github.com/awslabs/aws-lambda-powertools-java/pull/667) +* **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) ## [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/awslabs/aws-lambda-powertools-java/issues/654) +* **Tracing**: add `Service` annotation. [#654](https://github.com/aws-powertools/powertools-lambda-java/issues/654) ## [1.8.2] - 2021-12-15 @@ -151,12 +151,12 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ### Added -* **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/awslabs/aws-lambda-powertools-java/pull/560) -* **SQS Large message processing**: Ability to override the default `S3Client` use to fetch payload from S3. [#602](https://github.com/awslabs/aws-lambda-powertools-java/pull/602) +* **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) ### Regression -* **Logging**: `@Logging` annotation now works with `@Tracing` annotation on `RequestStreamHandler` when used in `logEvent` mode. [#567](https://github.com/awslabs/aws-lambda-powertools-java/pull/567) +* **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 @@ -164,25 +164,25 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [1.7.3] - 2021-09-14 -* **SQS Batch processing**: Ability to move non retryable message to configured dead letter queue(DLQ). [#500](https://github.com/awslabs/aws-lambda-powertools-java/pull/500) +* **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) ## [1.7.2] - 2021-08-03 * **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/awslabs/aws-lambda-powertools-java/pull/489) -* **Logging**: Performance optimisation to improve cold start. [#484](https://github.com/awslabs/aws-lambda-powertools-java/pull/484) -* **SQS Batch processing/Large message**: Module now lazy loads default SQS client. [#484](https://github.com/awslabs/aws-lambda-powertools-java/pull/484) +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) ## [1.7.1] - 2021-07-06 -* **Powertools for AWS Lambda (Java) All Modules**: Fix static code analysis violations done via [spotbugs](https://github.com/spotbugs/spotbugs) ([#458](https://github.com/awslabs/aws-lambda-powertools-java/pull/458)). +* **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)). ## [1.7.0] - 2021-07-05 ### Added -* **Logging**: Support for extracting Correlation id using `@Logging` annotation via `correlationIdPath` attribute and `setCorrelationId()` method in `LoggingUtils`([#448](https://github.com/awslabs/aws-lambda-powertools-java/pull/448)). -* **Logging**: New `clearState` attribute on `@Logging` annotation to clear previously added custom keys upon invocation([#453](https://github.com/awslabs/aws-lambda-powertools-java/pull/453)). +* **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 @@ -192,8 +192,8 @@ Users no longer need to depend on [com.nickwongdev](https://mvnrepository.com/ar ### Added -* **Tracing**: Support for Boolean and Number type as value in `TracingUtils.putAnnotation()`([#423](https://github.com/awslabs/aws-lambda-powertools-java/pull/432)). -* **Logging**: API to remove any additional custom key from logger entry using `LoggingUtils.removeKeys()`([#395](https://github.com/awslabs/aws-lambda-powertools-java/pull/395)). +* **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 @@ -202,7 +202,7 @@ Users no longer need to depend on [com.nickwongdev](https://mvnrepository.com/ar ## [1.5.0] - 2021-03-30 * **Metrics**: Ability to set multiple dimensions as default dimensions via `MetricsUtils.defaultDimensions()`. - Introduced in [v1.4.0](https://github.com/awslabs/aws-lambda-powertools-java/releases/tag/v1.4.0) + 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. ## [1.4.0] - 2021-03-11 diff --git a/README.md b/README.md index 04a0ad288..0b00be700 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # 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/awslabs/aws-lambda-powertools-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/awslabs/aws-lambda-powertools-java/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/awslabs/aws-lambda-powertools-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/lambda-java/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/aws-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/awslabs/aws-lambda-powertools-python), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet). -**[📜Documentation](https://awslabs.github.io/aws-lambda-powertools-java/)** | **[Feature request](https://github.com/awslabs/aws-lambda-powertools-java/issues/new?assignees=&labels=feature-request%2C+triage&template=feature_request.md&title=)** | **[🐛Bug Report](https://github.com/awslabs/aws-lambda-powertools-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/)** | **[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/)** ### Installation diff --git a/docs/core/logging.md b/docs/core/logging.md index 708607a9f..1af206314 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -466,7 +466,7 @@ via `samplingRate` attribute on annotation. ## Upgrade to JsonTemplateLayout from deprecated LambdaJsonLayout configuration in log4j2.xml -Prior to version [1.10.0](https://github.com/awslabs/aws-lambda-powertools-java/releases/tag/v1.10.0), only supported way of configuring `log4j2.xml` was via `<LambdaJsonLayout/>`. This plugin is +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. Below examples shows deprecated and new configuration of `log4j2.xml`. diff --git a/docs/index.md b/docs/index.md index 10726f0ab..ee5429a3d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ 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/awslabs/aws-lambda-powertools-java/actions/workflows/build.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) +![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. diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index c8dd59000..2f4774c94 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -827,7 +827,7 @@ Data would then be stored in DynamoDB like this: This utility provides an abstract base class, so that you can implement your choice of persistent storage layer. You can extend the `BasePersistenceStore` class and implement the abstract methods `getRecord`, `putRecord`, -`updateRecord` and `deleteRecord`. You can have a look at [`DynamoDBPersistenceStore`](https://github.com/awslabs/aws-lambda-powertools-java/blob/master/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java) as an implementation reference. +`updateRecord` and `deleteRecord`. You can have a look at [`DynamoDBPersistenceStore`](https://github.com/aws-powertools/powertools-lambda-java/blob/master/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java) as an implementation reference. !!! danger Pay attention to the documentation for each method - you may need to perform additional checks inside these methods to ensure the idempotency guarantees remain intact. diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 1101d28ad..ba8859027 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,6 +1,6 @@ # CoreUtilities -This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes [Powertools for AWS Lambda (Java) for operational best practices](https://github.com/awslabs/aws-lambda-powertools-java), and the following files and folders. +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes [Powertools for AWS Lambda (Java) for operational best practices](https://github.com/aws-powertools/powertools-lambda-java), and the following files and folders. - HelloWorldFunction/src/main - Code for the application's Lambda function. - events - Invocation events that you can use to invoke the function. @@ -119,20 +119,20 @@ aws cloudformation delete-stack --stack-name <Name-of-your-deployed-stack> **Tracing** -[Tracing utility](https://awslabs.github.io/aws-lambda-powertools-java/core/tracing/) provides functionality to reduce the overhead of performing common tracing tasks. It traces the execution of this sample code including the response and exceptions as tracing metadata - You can visualize them in AWS X-Ray. +[Tracing utility](https://docs.powertools.aws.dev/lambda-java/core/tracing/) provides functionality to reduce the overhead of performing common tracing tasks. It traces the execution of this sample code including the response and exceptions as tracing metadata - You can visualize them in AWS X-Ray. **Logger** -[Logging utility](https://awslabs.github.io/aws-lambda-powertools-java/core/logging/) creates an opinionated application Logger with structured logging as the output, dynamically samples a percentage (samplingRate) of your logs in DEBUG mode for concurrent invocations, log incoming events as your function is invoked, and injects key information from Lambda context object into your Logger - You can visualize them in Amazon CloudWatch Logs. +[Logging utility](https://docs.powertools.aws.dev/lambda-java/core/logging/) creates an opinionated application Logger with structured logging as the output, dynamically samples a percentage (samplingRate) of your logs in DEBUG mode for concurrent invocations, log incoming events as your function is invoked, and injects key information from Lambda context object into your Logger - You can visualize them in Amazon CloudWatch Logs. **Metrics** -[Metrics utility](https://awslabs.github.io/aws-lambda-powertools-java/core/metrics/) captures cold start metric of your Lambda invocation, and could add additional metrics to help you understand your application KPIs - You can visualize them in Amazon CloudWatch. +[Metrics utility](https://docs.powertools.aws.dev/lambda-java/core/metrics/) captures cold start metric of your Lambda invocation, and could add additional metrics to help you understand your application KPIs - You can visualize them in Amazon CloudWatch. ## Resources See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. -Check the [Powertools for AWS Lambda (Java)](https://awslabs.github.io/aws-lambda-powertools-java/) for more information on how to use and configure such tools +Check the [Powertools for AWS Lambda (Java)](https://docs.powertools.aws.dev/lambda-java/) for more information on how to use and configure such tools Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/examples/powertools-examples-idempotency/README.md b/examples/powertools-examples-idempotency/README.md index 29188d89f..278484a92 100644 --- a/examples/powertools-examples-idempotency/README.md +++ b/examples/powertools-examples-idempotency/README.md @@ -1,6 +1,6 @@ # Idempotency -This project contains an example of Lambda function using the idempotency module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/idempotency/). +This project contains an example of 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/). ## Deploy the sample application diff --git a/examples/powertools-examples-parameters/README.md b/examples/powertools-examples-parameters/README.md index a91dd44c9..0f843d455 100644 --- a/examples/powertools-examples-parameters/README.md +++ b/examples/powertools-examples-parameters/README.md @@ -1,6 +1,6 @@ # Parameters -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://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters/). +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/). ## Deploy the sample application diff --git a/examples/powertools-examples-serialization/README.md b/examples/powertools-examples-serialization/README.md index 3d02f1c32..7f70da1f2 100644 --- a/examples/powertools-examples-serialization/README.md +++ b/examples/powertools-examples-serialization/README.md @@ -1,6 +1,6 @@ # Deserialization -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://awslabs.github.io/aws-lambda-powertools-java/utilities/serialization/). +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/utilities/serialization/). ## Deploy the sample application diff --git a/examples/powertools-examples-validation/README.md b/examples/powertools-examples-validation/README.md index 898e5b60e..39afc48b9 100644 --- a/examples/powertools-examples-validation/README.md +++ b/examples/powertools-examples-validation/README.md @@ -1,6 +1,6 @@ # Validation -This project contains an example of Lambda function using the validation module of Powertools for AWS Lambda (Java). For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/validation/). +This project contains an example of Lambda function using the validation 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/validation/). ## Deploy the sample application diff --git a/mkdocs.yml b/mkdocs.yml index 613402914..73256756d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,5 +85,5 @@ extra: powertools: version: 1.15.0 -repo_url: https://github.com/awslabs/aws-lambda-powertools-java +repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/master/docs diff --git a/pom.xml b/pom.xml index f7846b1b5..c2142f854 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <licenses> <license> @@ -43,7 +43,7 @@ </modules> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 727c96e70..2d8b8d489 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -13,7 +13,7 @@ <version>1.15.0</version> </parent> - <name>AWS Lambda Powertools for Java library Cloudformation</name> + <name>Powertools for AWS Lambda (Java)library 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. @@ -21,14 +21,14 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> - <name>AWS Lambda Powertools team</name> + <name>Powertools for AWS Lambda (Java) Team</name> <organization>Amazon Web Services</organization> <organizationUrl>https://aws.amazon.com/</organizationUrl> </developer> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 7a690bd0b..6aa2414d1 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -20,11 +20,11 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 28f1be779..9dc20f784 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -10,8 +10,8 @@ </parent> <artifactId>powertools-e2e-tests</artifactId> - <name>AWS Lambda Powertools for Java library End-to-end tests</name> - <description>AWS Lambda Powertools for Java End-To-End Tests</description> + <name>Powertools for AWS Lambda (Java)library End-to-end tests</name> + <description>Powertools for AWS Lambda (Java)End-To-End Tests</description> <properties> <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> diff --git a/powertools-idempotency/README.md b/powertools-idempotency/README.md index 99b9c7ac8..141021e1d 100644 --- a/powertools-idempotency/README.md +++ b/powertools-idempotency/README.md @@ -1,5 +1,5 @@ ## Idempotency -Refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/idempotency/) for details on how to use this module in your Lambda function. +Refer to the [documentation](https://docs.powertools.aws.dev/lambda-java/utilities/idempotency/) for details on how to use this module in your Lambda function. ### Contributing This module provides a persistence layer with a built-in store using DynamoDB. diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index f3fb7059c..7c5b81e5d 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -20,10 +20,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index a044a4a34..ff4ab91b9 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -20,10 +20,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index a10f32629..345efe558 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -21,10 +21,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 3df5a9b0c..876aeddb1 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -20,10 +20,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> 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 index 03584738e..bf8d763b9 100644 --- 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 @@ -18,7 +18,7 @@ * 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://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters">Parameters provider documentation</a> + * @see <a href="https://docs.powertools.aws.dev/lambda-java/utilities/parameters">Parameters provider documentation</a> * */ public class DynamoDbProvider extends BaseProvider { 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 index e4c70acb0..3c4b2746a 100644 --- 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 @@ -137,7 +137,7 @@ private static <T extends BaseProvider> T createProvider(Class<T> providerClass) return provider; } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { throw new RuntimeException("Unexpected error occurred. Please raise issue at " + - "https://github.com/awslabs/aws-lambda-powertools-java/issues", e); + "https://github.com/aws-powertools/powertools-lambda-java/issues", e); } } diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e68b26f7b..1b6589c43 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -20,10 +20,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 4289a666a..035bbc636 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -20,10 +20,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> 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 index 9ae81b77d..3461c6755 100644 --- 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 @@ -519,7 +519,7 @@ private static <R> SqsMessageHandler<R> instantiatedHandler(final Class<? extend } catch (Exception e) { LOG.error("Failed creating handler instance", e); throw new RuntimeException("Unexpected error occurred. Please raise issue at " + - "https://github.com/awslabs/aws-lambda-powertools-java/issues", e); + "https://github.com/aws-powertools/powertools-lambda-java/issues", e); } } diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 2cd323f41..7b5c396a6 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -20,10 +20,10 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index cf1962530..434d2d048 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -20,11 +20,11 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index fbd076f0a..e9e035a65 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -20,11 +20,11 @@ <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> - <url>https://github.com/awslabs/aws-lambda-powertools-java/issues</url> + <url>https://github.com/aws-powertools/powertools-lambda-java/issues</url> </issueManagement> <scm> - <url>https://github.com/awslabs/aws-lambda-powertools-java.git</url> + <url>https://github.com/aws-powertools/powertools-lambda-java.git</url> </scm> <developers> <developer> From 9aa4bbd9ed1a1bdbfb55deaefc0031dcb5cc1b21 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:30:03 +0000 Subject: [PATCH 0319/1008] fix: update references to other variants --- .github/ISSUE_TEMPLATE/maintenance.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml index 7a8b50c9a..843f5103c 100644 --- a/.github/ISSUE_TEMPLATE/maintenance.yml +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -57,7 +57,7 @@ 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/awslabs/aws-lambda-powertools-python/), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript/) + - 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/) required: false - type: markdown attributes: diff --git a/README.md b/README.md index 0b00be700..cef8976d5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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/awslabs/aws-lambda-powertools-python), [TypeScript](https://github.com/awslabs/aws-lambda-powertools-typescript), and [.NET](https://github.com/awslabs/aws-lambda-powertools-dotnet). +> 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/)** From 2923a46d051f8cadb6509d66254851bf6e4a1034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:12:08 +0200 Subject: [PATCH 0320/1008] change from master to main (#1213) --- .github/workflows/build-docs.yml | 4 ++-- .github/workflows/build.yml | 4 ++-- .github/workflows/release-drafter.yml | 2 +- .github/workflows/spotbugs.yml | 2 +- CHANGELOG.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 2 +- mkdocs.yml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 97ad0a44e..cbcdcdd9e 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -3,7 +3,7 @@ name: Build Docs on: pull_request: branches: - - master + - main paths: - 'docs/**' - 'mkdocs.yml' @@ -11,7 +11,7 @@ on: push: branches: - - master + - main paths: - 'docs/**' - 'mkdocs.yml' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ffc7b024..b2931afbf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Build on: pull_request: branches: - - master + - main paths: - 'powertools-cloudformation/**' - 'powertools-core/**' @@ -23,7 +23,7 @@ on: - '.github/workflows/**' push: branches: - - master + - main paths: - 'powertools-cloudformation/**' - 'powertools-core/**' diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index e627dfd3c..5b27fd671 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -4,7 +4,7 @@ on: push: # branches to consider in the event; optional, defaults to all branches: - - master + - main jobs: update_release_draft: diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 25dced078..561300810 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -3,7 +3,7 @@ name: SpotBugs on: pull_request: branches: - - master + - main paths: - 'powertools-cloudformation/**' - 'powertools-core/**' diff --git a/CHANGELOG.md b/CHANGELOG.md index fda9bc732..9055d2c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -208,7 +208,7 @@ Users no longer need to depend on [com.nickwongdev](https://mvnrepository.com/ar ## [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/master/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), + **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()`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 914e0741d..08aeddcfe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ reported the issue. Please try to include as much information as you can. Detail ## Contributing via Pull Requests Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *master* branch. +1. You are working against the latest source on the *main* branch. 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. diff --git a/README.md b/README.md index cef8976d5..07efb38a0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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/lambda-java/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/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/lambda-java/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/lambda-java) Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. diff --git a/mkdocs.yml b/mkdocs.yml index 73256756d..69d9c57db 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -86,4 +86,4 @@ extra: version: 1.15.0 repo_url: https://github.com/aws-powertools/powertools-lambda-java -edit_uri: edit/master/docs +edit_uri: edit/main/docs From 24767cf1d8023b56445f65c73752f5861738f322 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:32:10 +0200 Subject: [PATCH 0321/1008] build(deps): bump jackson-databind from 2.14.2 to 2.15.2 (#1207) * build(deps): bump jackson-databind from 2.14.2 to 2.15.2 --- pom.xml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c2142f854..0597eb836 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ <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.14.2</jackson.version> + <jackson.version>2.15.2</jackson.version> <aspectj.version>1.9.7</aspectj.version> <aws.sdk.version>2.20.86</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> @@ -166,6 +166,16 @@ <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + <version>${jackson.version}</version> + </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> From 561bdefb4a6e55a704e333766ed5dd0dd04491b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:44:43 +0200 Subject: [PATCH 0322/1008] build(deps): bump payloadoffloading-common from 2.1.2 to 2.1.3 (#1206) Bumps [payloadoffloading-common](https://github.com/awslabs/payload-offloading-java-common-lib-for-aws) from 2.1.2 to 2.1.3. - [Release notes](https://github.com/awslabs/payload-offloading-java-common-lib-for-aws/releases) - [Commits](https://github.com/awslabs/payload-offloading-java-common-lib-for-aws/compare/v2.1.2...v2.1.3) --- updated-dependencies: - dependency-name: software.amazon.payloadoffloading:payloadoffloading-common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0597eb836..0266f8db5 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ <aspectj.version>1.9.7</aspectj.version> <aws.sdk.version>2.20.86</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> - <payloadoffloading-common.version>2.1.2</payloadoffloading-common.version> + <payloadoffloading-common.version>2.1.3</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.1</lambda.events.version> From f8a18421bf9610cc13584a10e1a573a7a1a616cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 15:55:16 +0200 Subject: [PATCH 0323/1008] build(deps): bump aws.sdk.version from 2.20.86 to 2.20.88 (#1214) Bumps `aws.sdk.version` from 2.20.86 to 2.20.88. Updates `software.amazon.awssdk:bom` from 2.20.86 to 2.20.88 Updates `http-client-spi` from 2.20.86 to 2.20.88 Updates `url-connection-client` from 2.20.86 to 2.20.88 Updates `s3` from 2.20.86 to 2.20.88 Updates `lambda` from 2.20.86 to 2.20.88 Updates `cloudwatch` from 2.20.86 to 2.20.88 Updates `xray` from 2.20.86 to 2.20.88 Updates `cloudformation` from 2.20.86 to 2.20.88 Updates `sts` from 2.20.86 to 2.20.88 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 83588291a..680e62d1c 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.86</version> + <version>2.20.88</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 0266f8db5..93e4d9fbc 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.86</aws.sdk.version> + <aws.sdk.version>2.20.88</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 55626ce2d6e8220e8a15c5c2f097a692b7d2281a Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:51:05 +0200 Subject: [PATCH 0324/1008] fix: remove GH pages (#1211) --- .github/workflows/docs.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b8eeab7e7..9c09e294d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,11 +29,6 @@ jobs: - name: Build docs website run: | make build-docs-website - - name: Deploy all docs - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef with: From 3dc3a56963bbb9adab22eddc787915b4fa39210b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 17:38:24 +0200 Subject: [PATCH 0325/1008] build(deps): bump aws-lambda-java-events from 3.11.1 to 3.11.2 (#1215) * build(deps): bump aws-lambda-java-events from 3.11.1 to 3.11.2 * fix idempotency tests --- examples/powertools-examples-core/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- .../powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- .../persistence/BasePersistenceStoreTest.java | 28 +++++++++---------- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 7338ba665..e6ccd3918 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.1</version> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 130eadd6a..60f4b012b 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.1</version> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 9e9ffe1b0..adcdbf79a 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -30,7 +30,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.1</version> + <version>3.11.2</version> </dependency> <!-- Test dependencies --> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 663297dd2..92d17ac06 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -30,7 +30,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.1</version> + <version>3.11.2</version> </dependency> <!-- Test dependencies --> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 680e62d1c..44c84956d 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.1</version> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/pom.xml b/pom.xml index 93e4d9fbc..264227233 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ <payloadoffloading-common.version>2.1.3</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.1</lambda.events.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> <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index 47e5df1ab..6b58fa8a5 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -88,7 +88,7 @@ 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); assertThat(dr.getPayloadHash()).isEqualTo(""); assertThat(dr.getInProgressExpiryTimestamp()).isEmpty(); assertThat(status).isEqualTo(1); @@ -105,7 +105,7 @@ 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); assertThat(dr.getPayloadHash()).isEqualTo(""); assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo(now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); assertThat(status).isEqualTo(1); @@ -216,7 +216,7 @@ 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); assertThat(dr.getPayloadHash()).isEqualTo(""); assertThat(status).isEqualTo(2); assertThat(cache).isEmpty(); @@ -235,11 +235,11 @@ public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessi assertThat(status).isEqualTo(2); assertThat(cache).hasSize(1); - DataRecord record = cache.get("testFunction#47261bd5b456f400f8d191cfb3a7482f"); + 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#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); assertThat(record.getPayloadHash()).isEqualTo(""); } @@ -257,7 +257,7 @@ public void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItem Instant now = Instant.now(); DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(record.getResponseData()).isEqualTo("Response"); assertThat(status).isEqualTo(0); @@ -272,15 +272,15 @@ public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throw Instant now = Instant.now(); DataRecord dr = new DataRecord( - "testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", + "testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "result of the function", null); - cache.put("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", dr); + cache.put("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", dr); DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); assertThat(record.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); assertThat(record.getResponseData()).isEqualTo("result of the function"); assertThat(status).isEqualTo(-1); // getRecord must not be called (retrieve from cache) @@ -295,15 +295,15 @@ public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() th Instant now = Instant.now(); DataRecord dr = new DataRecord( - "testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", + "testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), "result of the function", null); - cache.put("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f", dr); + cache.put("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", dr); DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#47261bd5b456f400f8d191cfb3a7482f"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(record.getResponseData()).isEqualTo("Response"); assertThat(status).isEqualTo(0); @@ -347,8 +347,8 @@ public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { persistenceStore.configure(IdempotencyConfig.builder() .withUseLocalCache(true).build(), null, cache); - cache.put("testFunction#47261bd5b456f400f8d191cfb3a7482f", - new DataRecord("testFunction#47261bd5b456f400f8d191cfb3a7482f", DataRecord.Status.COMPLETED, 123, null, null)); + cache.put("testFunction#7b40f56c086de5aa91dc467456329ed2", + new DataRecord("testFunction#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, 123, null, null)); persistenceStore.deleteRecord(JsonConfig.get().getObjectMapper().valueToTree(event), new ArithmeticException()); assertThat(status).isEqualTo(3); assertThat(cache).isEmpty(); From 5dc92c3147a0b3447c495d7001f34e3e4d7d8a92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 17:42:50 +0200 Subject: [PATCH 0326/1008] build(deps): bump maven-surefire-plugin from 3.1.0 to 3.1.2 (#1194) * build(deps): bump maven-surefire-plugin from 3.1.0 to 3.1.2 --- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 60f4b012b..c872ee905 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -127,7 +127,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.0</version> + <version>3.1.2</version> <configuration> <systemPropertyVariables> <sqlite4java.library.path>${project.build.directory}/native-libs</sqlite4java.library.path> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index adcdbf79a..ac7d2176c 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -60,7 +60,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.1.0</version> + <version>3.1.2</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 92d17ac06..b3c7d70f7 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -60,7 +60,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.1.0</version> + <version>3.1.2</version> </plugin> </plugins> </build> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 2f42ec370..f63a54b57 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -55,7 +55,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.1.0</version> + <version>3.1.2</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/pom.xml b/pom.xml index 264227233..bc04fb5f0 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> - <maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version> + <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7c5b81e5d..239595c22 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -193,7 +193,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.0</version> + <version>3.1.2</version> <configuration> <argLine> --add-opens java.base/java.util=ALL-UNNAMED From 8b9ec5727d12de696040d45af302f885d9eee342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 20 Jun 2023 17:44:39 +0200 Subject: [PATCH 0327/1008] chore: add all java versions and use corretto for build (#1191) --- .github/workflows/build.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2931afbf..38f99bd4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,13 +42,12 @@ on: - 'examples/pom.xml' - '.github/workflows/**' jobs: - build: + build-corretto: runs-on: ubuntu-latest strategy: - max-parallel: 4 + max-parallel: 5 matrix: - # test against latest update of each major Java version, as well as specific updates of LTS versions: - java: [8, 8.0.192, 11.0.x, 11.0.3, 12, 13, 15, 16, 17 ] + java: [8, 11, 15, 16, 17, 18, 19, 20 ] name: Java ${{ matrix.java }} env: JAVA: ${{ matrix.java }} @@ -56,16 +55,16 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: - distribution: 'zulu' + distribution: 'corretto' java-version: ${{ matrix.java }} cache: 'maven' - name: Build with Maven run: mvn -Pbuild-without-spotbugs -B package --file pom.xml - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # 3.1.1 - if: ${{ matrix.java == '11.0.x' }} # publish results once + 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: From 68d6a49d646bd4cca3f0290362bf8dac302bbaab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 18:01:19 +0200 Subject: [PATCH 0328/1008] build(deps): bump jacoco-maven-plugin from 0.8.8 to 0.8.10 (#1203) * build(deps): bump jacoco-maven-plugin from 0.8.8 to 0.8.10 * add opens for jdk16+ on surefire --- pom.xml | 23 ++++++++++++++++++++++- powertools-idempotency/pom.xml | 23 ----------------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index bc04fb5f0..ea57585e3 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.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.8</jacoco-maven-plugin.version> + <jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.8</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> @@ -544,6 +544,27 @@ </plugins> </build> </profile> + <profile> + <id>jdk16</id> + <activation> + <jdk>[16,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <argLine> + --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-idempotency/pom.xml b/powertools-idempotency/pom.xml index 239595c22..a49a70c40 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -182,29 +182,6 @@ </plugin> </plugins> </build> - <profiles> - <profile> - <id>jdk16</id> - <activation> - <jdk>[16,)</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> - <configuration> - <argLine> - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - </argLine> - </configuration> - </plugin> - </plugins> - </build> - </profile> - </profiles> <repositories> <repository> <id>dynamodb-local-oregon</id> From 0de30347861dac01e2fc38cc1cb93e5a6b4d0593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 20 Jun 2023 18:05:03 +0200 Subject: [PATCH 0329/1008] chore: E2E tests GitHub action (#1175) --- .github/workflows/run-e2e-tests.yml | 48 +++++++++++++++++++ pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- .../lambda/powertools/IdempotencyE2ET.java | 6 ++- 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/run-e2e-tests.yml diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml new file mode 100644 index 000000000..2d9324ef0 --- /dev/null +++ b/.github/workflows/run-e2e-tests.yml @@ -0,0 +1,48 @@ +name: Run end-to-end tests + +on: + workflow_dispatch: + + push: + branches: [main] + 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/**' + - 'pom.xml' + - '.github/workflows/**' + +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 -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file diff --git a/pom.xml b/pom.xml index ea57585e3..53c9762f9 100644 --- a/pom.xml +++ b/pom.xml @@ -296,7 +296,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.4.7</version> + <version>1.3.8</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 9dc20f784..eae7d4a22 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -29,7 +29,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.4.7</version> + <version>1.3.8</version> </dependency> <dependency> 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 4133f986f..6923c3caa 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 @@ -10,6 +10,7 @@ import java.time.Year; import java.util.Collections; +import java.util.UUID; import java.util.concurrent.TimeUnit; import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; @@ -21,11 +22,12 @@ public class IdempotencyE2ET { @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) public static void setup() { + String random = UUID.randomUUID().toString().substring(0, 6); infrastructure = Infrastructure.builder() .testName(IdempotencyE2ET.class.getSimpleName()) .pathToFunction("idempotency") - .idempotencyTable("idempo") - .environmentVariables(Collections.singletonMap("IDEMPOTENCY_TABLE", "idempo")) + .idempotencyTable("idempo" + random) + .environmentVariables(Collections.singletonMap("IDEMPOTENCY_TABLE", "idempo" + random)) .build(); functionName = infrastructure.deploy(); } From 90a86c5937cd7d3dde1de850e8d2aba54ac8bb56 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:56:20 +0800 Subject: [PATCH 0330/1008] feat: Add AppConfig provider to parameters module (#1104) --- docs/utilities/parameters.md | 43 ++++ powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- .../handlers/parameters/pom.xml | 61 +++++ .../lambda/powertools/e2e/Function.java | 22 ++ .../amazon/lambda/powertools/e2e/Input.java | 34 +++ .../parameters/src/main/resources/log4j2.xml | 16 ++ powertools-e2e-tests/handlers/pom.xml | 6 + .../lambda/powertools/ParametersE2ET.java | 76 ++++++ .../powertools/testutils/AppConfig.java | 34 +++ .../powertools/testutils/Infrastructure.java | 79 ++++++ powertools-parameters/pom.xml | 5 + .../parameters/AppConfigProvider.java | 228 ++++++++++++++++++ .../powertools/parameters/ParamManager.java | 35 +++ .../parameters/AppConfigProviderTest.java | 148 ++++++++++++ .../ParamManagerIntegrationTest.java | 14 +- 15 files changed, 801 insertions(+), 2 deletions(-) create mode 100644 powertools-e2e-tests/handlers/parameters/pom.xml create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java create mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java create mode 100644 powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 9041ac08e..7e70248d4 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -214,6 +214,49 @@ a `DynamoDbProvider` providing a client if you need to configure it yourself. } ``` +## 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" + + ```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"); + + // 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 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(); + + // Get an instance of the DynamoDbProvider + AppConfigProvider appConfigProvider = ParamManager.getAppConfigProvider(appConfigDataClient, "my-environment", "my-app"); + + // Retrieve a single parameter + String value = appConfigProvider.get("my-key"); + } + ``` ## Advanced configuration diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 723ad75c5..35db53899 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -10,7 +10,7 @@ <artifactId>e2e-test-handler-metrics</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools metrics</name> + <name>A Lambda function using Powertools for AWS Lambda (Java) Parameters</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml new file mode 100644 index 000000000..baaedb324 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -0,0 +1,61 @@ +<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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-parameters</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using powertools logging</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</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> +</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 new file mode 100644 index 000000000..f7f784f78 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,22 @@ +package software.amazon.lambda.powertools.e2e; + +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.parameters.ParamManager; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.AppConfigProvider; + +public class Function implements RequestHandler<Input, String> { + + private static final Logger LOG = LogManager.getLogger(Function.class); + + @Logging + public String handleRequest(Input input, Context context) { + AppConfigProvider provider = ParamManager.getAppConfigProvider(input.getEnvironment(), input.getApp()); + return provider.get(input.getKey()); + + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..7ea22143f --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,34 @@ +package software.amazon.lambda.powertools.e2e; + +import java.util.Map; + +public class Input { + + private String app; + private String environment; + private String key; + public void setApp(String app) { + this.app = app; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setKey(String key) { + this.key = key; + } + + public String getApp() { + return app; + } + + public String getEnvironment() { + return environment; + } + + public String getKey() { + return key; + } + +} diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/parameters/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/parameters/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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index be53a9a5b..a804e48eb 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -27,6 +27,7 @@ <module>tracing</module> <module>metrics</module> <module>idempotency</module> + <module>parameters</module> </modules> <dependencyManagement> @@ -51,6 +52,11 @@ <artifactId>powertools-idempotency</artifactId> <version>${lambda.powertools.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> 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 new file mode 100644 index 000000000..5abbb98df --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java @@ -0,0 +1,76 @@ +package software.amazon.lambda.powertools; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.*; +import software.amazon.lambda.powertools.testutils.AppConfig; +import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class ParametersE2ET { + + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private Infrastructure infrastructure; + private String functionName; + private final AppConfig appConfig; + + public ParametersE2ET() { + Map<String,String> params = new HashMap<>(); + params.put("key1", "value1"); + params.put("key2", "value2"); + appConfig = new AppConfig("e2eApp", "e2etest", params); + } + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + public 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()} + }) + .collect(Collectors.toMap(data -> data[0], data -> data[1]))) + .build(); + functionName = infrastructure.deploy(); + } + + @AfterAll + public void tearDown() { + if (infrastructure != null) + infrastructure.destroy(); + } + + @Test + public void test_getAppConfigValue() { + for (Map.Entry<String, String >configKey: appConfig.getConfigurationValues().entrySet()) { + + // Arrange + String event1 = "{" + + "\"app\": \"" + appConfig.getApplication() + "\", " + + "\"environment\": \"" + appConfig.getEnvironment() + "\", " + + "\"key\": \"" + configKey.getKey() + "\"" + + "}"; + + // Act + InvocationResult invocationResult = invokeFunction(functionName, event1); + + // Assert + assertThat(invocationResult.getResult()).isEqualTo("\"" + configKey.getValue() + "\""); + } + } + +} diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java new file mode 100644 index 000000000..c87f4ac48 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java @@ -0,0 +1,34 @@ +package software.amazon.lambda.powertools.testutils; + +import java.util.HashMap; +import java.util.Map; + +/** + * Defines configuration used to setup an AppConfig + * deployment when the infrastructure is rolled out. + * + * All fields are non-nullable. + */ +public class AppConfig { + private String application; + private String environment; + private Map<String, String> configurationValues; + + public AppConfig(String application, String environment, Map<String, String> configurationValues) { + this.application = application; + this.environment = environment; + this.configurationValues = configurationValues; + } + + public String getApplication() { + return application; + } + + public String getEnvironment() { + return environment; + } + + public Map<String, String> getConfigurationValues() { + return configurationValues; + } +} 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 1fdbd3836..59035af7c 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 @@ -7,12 +7,17 @@ import software.amazon.awscdk.Stack; import software.amazon.awscdk.*; import software.amazon.awscdk.cxapi.CloudAssembly; +import software.amazon.awscdk.services.appconfig.*; 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.Table; +import software.amazon.awscdk.services.groundstation.CfnConfig; +import software.amazon.awscdk.services.iam.PolicyStatement; +import software.amazon.awscdk.services.iam.ServicePrincipal; import software.amazon.awscdk.services.lambda.Code; import software.amazon.awscdk.services.lambda.Function; +import software.amazon.awscdk.services.lambda.Permission; import software.amazon.awscdk.services.lambda.Tracing; import software.amazon.awscdk.services.logs.LogGroup; import software.amazon.awscdk.services.logs.RetentionDays; @@ -65,6 +70,9 @@ public class Infrastructure { private final Region region; private final String account; private final String idempotencyTable; + private final AppConfig appConfig; + + private String functionName; private Object cfnTemplate; private String cfnAssetDirectory; @@ -78,6 +86,7 @@ private Infrastructure(Builder builder) { this.timeout = builder.timeoutInSeconds; this.pathToFunction = builder.pathToFunction; this.idempotencyTable = builder.idemPotencyTable; + this.appConfig = builder.appConfig; this.app = new App(); this.stack = createStackWithLambda(); @@ -140,6 +149,7 @@ public static class Builder { public long timeoutInSeconds = 30; public String pathToFunction; public String testName; + public AppConfig appConfig; private String stackName; private boolean tracing = false; private JavaRuntime runtime; @@ -200,6 +210,11 @@ public Builder idempotencyTable(String tableName) { return this; } + public Builder appConfig(AppConfig app) { + this.appConfig = app; + return this; + } + public Builder environmentVariables(Map<String, String> environmentVariables) { this.environmentVariables = environmentVariables; return this; @@ -277,9 +292,73 @@ private Stack createStackWithLambda() { .tableName(idempotencyTable) .timeToLiveAttribute("expiration") .build(); + table.grantReadWriteData(function); } + if (appConfig != null) { + CfnApplication app = CfnApplication.Builder + .create(stack, "AppConfigApp") + .name(appConfig.getApplication()) + .build(); + + CfnEnvironment environment = CfnEnvironment.Builder + .create(stack, "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") + .name("FastDeploymentStrategy") + .deploymentDurationInMinutes(0) + .finalBakeTimeInMinutes(0) + .growthFactor(100) + .replicateTo("NONE") + .build(); + + // Get the lambda permission to use AppConfig + function.addToRolePolicy(PolicyStatement.Builder + .create() + .actions(singletonList("appconfig:*")) + .resources(singletonList("*")) + .build() + ); + + CfnDeployment previousDeployment = null; + for (Map.Entry<String,String> entry : appConfig.getConfigurationValues().entrySet()) { + CfnConfigurationProfile configProfile = CfnConfigurationProfile.Builder + .create(stack, "AppConfigProfileFor" + entry.getKey()) + .applicationId(app.getRef()) + .locationUri("hosted") + .name(entry.getKey()) + .build(); + + CfnHostedConfigurationVersion configVersion = CfnHostedConfigurationVersion.Builder + .create(stack, "AppConfigHostedVersionFor" + entry.getKey()) + .applicationId(app.getRef()) + .contentType("text/plain") + .configurationProfileId(configProfile.getRef()) + .content(entry.getValue()) + .build(); + + CfnDeployment deployment = CfnDeployment.Builder + .create(stack, "AppConfigDepoymentFor" + entry.getKey()) + .applicationId(app.getRef()) + .environmentId(environment.getRef()) + .deploymentStrategyId(fastDeployment.getRef()) + .configurationProfileId(configProfile.getRef()) + .configurationVersion(configVersion.getRef()) + .build(); + + // We need to chain the deployments to keep CFN happy + if (previousDeployment != null) { + deployment.addDependsOn(previousDeployment); + } + previousDeployment = deployment; + } + } return stack; } diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 876aeddb1..d1fb10183 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -81,6 +81,11 @@ <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> 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 new file mode 100644 index 000000000..90f30cb0e --- /dev/null +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java @@ -0,0 +1,228 @@ +package software.amazon.lambda.powertools.parameters; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +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.AppConfigDataClientBuilder; +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.awssdk.services.ssm.SsmClient; +import software.amazon.awssdk.services.ssm.SsmClientBuilder; +import software.amazon.lambda.powertools.core.internal.LambdaConstants; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +import java.util.HashMap; +import java.util.Map; + +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; + +/** + * 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. + * + * 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://awslabs.github.io/aws-lambda-powertools-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 static class EstablishedSession { + private final String nextSessionToken; + private final String lastConfigurationValue; + + private EstablishedSession(String nextSessionToken, String value) { + this.nextSessionToken = nextSessionToken; + this.lastConfigurationValue = value; + } + } + + 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; + } + + + /** + * 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"); + } + + /** + * 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(); + } + + 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) { + AppConfigDataClientBuilder appConfigDataClientBuilder = AppConfigDataClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { + appConfigDataClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + + client = appConfigDataClientBuilder.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/ParamManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java index 3c4b2746a..96cbabd0e 100644 --- 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 @@ -13,6 +13,7 @@ */ package software.amazon.lambda.powertools.parameters; +import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.ssm.SsmClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -81,6 +82,24 @@ public static DynamoDbProvider getDynamoDbProvider(String tableName) { .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. @@ -120,6 +139,22 @@ public static DynamoDbProvider getDynamoDbProvider(DynamoDbClient client, String .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; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java new file mode 100644 index 000000000..d72a1f042 --- /dev/null +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java @@ -0,0 +1,148 @@ +package software.amazon.lambda.powertools.parameters; + +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 software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +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.awssdk.services.appconfigdata.model.StartConfigurationSessionResponse; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.MockitoAnnotations.openMocks; + +public class AppConfigProviderTest { + + @Mock + AppConfigDataClient client; + + private AppConfigProvider provider; + + @Captor + ArgumentCaptor<StartConfigurationSessionRequest> startSessionRequestCaptor; + + @Captor + ArgumentCaptor<GetLatestConfigurationRequest> getLatestConfigurationRequestCaptor; + private final String environmentName = "test"; + + private final String applicationName = "fakeApp"; + + private final String defaultTestKey = "key1"; + + @BeforeEach + public void init() { + openMocks(this); + provider = AppConfigProvider.builder() + .withClient(client) + .withApplication(applicationName) + .withEnvironment(environmentName) + .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 + * start of a session. This means the provider must cache the result and return it again if it gets no data, but + * subsequent calls should once again return the new data. + */ + @Test + public void getValueRetrievesValue() { + // Arrange + StartConfigurationSessionResponse firstSession = StartConfigurationSessionResponse.builder() + .initialConfigurationToken("token1") + .build(); + // first response returns 'value1' + GetLatestConfigurationResponse firstResponse = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token2") + .configuration(SdkBytes.fromUtf8String("value1")) + .build(); + // Second response returns 'value2' + GetLatestConfigurationResponse secondResponse = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token3") + .configuration(SdkBytes.fromUtf8String("value2")) + .build(); + // Third response returns nothing, which means the provider should yield the previous value again + GetLatestConfigurationResponse thirdResponse = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token4") + .build(); + Mockito.when(client.startConfigurationSession(startSessionRequestCaptor.capture())) + .thenReturn(firstSession); + Mockito.when(client.getLatestConfiguration(getLatestConfigurationRequestCaptor.capture())) + .thenReturn(firstResponse, secondResponse, thirdResponse); + + // Act + String returnedValue1 = provider.getValue(defaultTestKey); + String returnedValue2 = provider.getValue(defaultTestKey); + String returnedValue3 = provider.getValue(defaultTestKey); + + // 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(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo(firstSession.initialConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo(firstResponse.nextPollConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(2).configurationToken()).isEqualTo(secondResponse.nextPollConfigurationToken()); + } + + /** + * If we mix requests for different keys together through the same provider, retrieval should + * work as expected. This means two separate configuration sessions should be established with AppConfig. + */ + @Test + public void multipleKeysRetrievalWorks() { + // Arrange + String param1Key = "key1"; + StartConfigurationSessionResponse param1Session = StartConfigurationSessionResponse.builder() + .initialConfigurationToken("token1a") + .build(); + GetLatestConfigurationResponse param1Response = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token1b") + .configuration(SdkBytes.fromUtf8String("value1")) + .build(); + String param2Key = "key2"; + StartConfigurationSessionResponse param2Session = StartConfigurationSessionResponse.builder() + .initialConfigurationToken("token2a") + .build(); + GetLatestConfigurationResponse param2Response = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token2b") + .configuration(SdkBytes.fromUtf8String("value1")) + .build(); + Mockito.when(client.startConfigurationSession(startSessionRequestCaptor.capture())) + .thenReturn(param1Session, param2Session); + Mockito.when(client.getLatestConfiguration(getLatestConfigurationRequestCaptor.capture())) + .thenReturn(param1Response, param2Response); + + // Act + String firstKeyValue = provider.getValue(param1Key); + String secondKeyValue = provider.getValue(param2Key); + + // Assert + assertThat(firstKeyValue).isEqualTo(param1Response.configuration().asUtf8String()); + assertThat(secondKeyValue).isEqualTo(param2Response.configuration().asUtf8String()); + assertThat(startSessionRequestCaptor.getAllValues().get(0).configurationProfileIdentifier()).isEqualTo(param1Key); + assertThat(startSessionRequestCaptor.getAllValues().get(1).configurationProfileIdentifier()).isEqualTo(param2Key); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo(param1Session.initialConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo(param2Session.initialConfigurationToken()); + + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java index ec1672ead..fca0a9362 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java @@ -19,6 +19,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +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.secretsmanager.model.GetSecretValueRequest; @@ -45,6 +46,9 @@ public class ParamManagerIntegrationTest { @Mock DynamoDbClient ddbClient; + @Mock + private AppConfigDataClient appConfigDataClient; + @Captor ArgumentCaptor<GetParameterRequest> ssmParamCaptor; @@ -57,7 +61,6 @@ public class ParamManagerIntegrationTest { @Captor ArgumentCaptor<GetSecretValueRequest> secretsCaptor; - @BeforeEach public void setup() throws IllegalAccessException { openMocks(this); @@ -129,7 +132,16 @@ public void getDynamoDbProvider() { // Assert assertThat(provider).isNotNull(); + } + + @Test + public void getAppConfigProvider() { + + // Act + AppConfigProvider provider = ParamManager.getAppConfigProvider(appConfigDataClient, "test-env", "test-app"); + // Assert + assertThat(provider).isNotNull(); } } From a3f08e00bacad10ba360076510b953ea87edc554 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub <kozubg@gmail.com> Date: Wed, 21 Jun 2023 09:04:45 +0200 Subject: [PATCH 0331/1008] Fix Dependabot configuration to ignore latest version of Mockito as it requires Java 11 (#1216) --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 743eaa616..4832b1c49 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,5 +9,5 @@ updates: - "dependencies" ignore: # Ignore Mockito 5.X.X as it does not support Java 8 - - dependency-name: mockito-* + - dependency-name: "org.mockito:mockito-*" update-types: ["version-update:semver-major"] \ No newline at end of file From a439002d4a7685575084becab018eaf3628d7ae2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 09:15:51 +0200 Subject: [PATCH 0332/1008] build(deps-dev): bump constructs from 10.2.47 to 10.2.55 (#1219) Bumps [constructs](https://github.com/aws/constructs) from 10.2.47 to 10.2.55. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.47...v10.2.55) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index eae7d4a22..e497f568f 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> - <constructs.version>10.2.47</constructs.version> + <constructs.version>10.2.55</constructs.version> <cdk.version>2.84.0</cdk.version> <!-- Don't deploy the e2e tests --> From fd467352ae7903a53b78ca99f6b5c0f7a1375fba Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:31:04 +0200 Subject: [PATCH 0333/1008] fix: codecov URL (#1222) * Update README.md Fix codecov --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07efb38a0..a79272d36 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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/lambda-java/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/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) Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. From 7f804fe3dd46540594b57fcd32bc9d8169efd03a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:13:37 +0200 Subject: [PATCH 0334/1008] build(deps): bump nexus-staging-maven-plugin from 1.6.8 to 1.6.13 (#1223) Bumps nexus-staging-maven-plugin from 1.6.8 to 1.6.13. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 53c9762f9..f4b92d024 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <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> - <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-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> From 31bebb740f27bac143151a715cc8d806fbf1891f Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:38:24 +0200 Subject: [PATCH 0335/1008] test: Roll SLF4J log4j bindings to v2 (#1190) * Roll SLF4J log4j bindings to v2 --- pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index f4b92d024..dd2ba70ac 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j-impl</artifactId> + <artifactId>log4j-slf4j2-impl</artifactId> <version>${log4j.version}</version> </dependency> <dependency> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index ff4ab91b9..dc9d7a0dc 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -63,7 +63,7 @@ </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j-impl</artifactId> + <artifactId>log4j-slf4j2-impl</artifactId> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 1b6589c43..2c507a425 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -51,7 +51,7 @@ </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j-impl</artifactId> + <artifactId>log4j-slf4j2-impl</artifactId> </dependency> <!-- Test dependencies --> From 4db9822ae433b1bf63f6412ede7cc58766a334c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 22 Jun 2023 11:23:39 +0200 Subject: [PATCH 0336/1008] fix: e2e tests on JDK8 (#1225) * fix e2e tests on JDK8 --- .github/workflows/run-e2e-tests.yml | 2 +- .../handlers/parameters/pom.xml | 2 +- .../lambda/powertools/e2e/Function.java | 7 +- powertools-e2e-tests/handlers/pom.xml | 107 ++++++++++++++++-- powertools-e2e-tests/pom.xml | 5 +- .../lambda/powertools/ParametersE2ET.java | 9 +- 6 files changed, 104 insertions(+), 28 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 2d9324ef0..bc5ffd437 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -45,4 +45,4 @@ jobs: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Run e2e test with Maven - run: mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file + run: mvn -Pbuild-without-spotbugs -DskipTests install --file pom.xml && mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index baaedb324..410cdfb5e 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -31,7 +31,7 @@ <build> <plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <configuration> <source>${maven.compiler.source}</source> 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 f7f784f78..a2d6a1ba6 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 @@ -2,17 +2,12 @@ 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.parameters.ParamManager; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.AppConfigProvider; +import software.amazon.lambda.powertools.parameters.ParamManager; public class Function implements RequestHandler<Input, String> { - private static final Logger LOG = LogManager.getLogger(Function.class); - @Logging public String handleRequest(Input input, Context context) { AppConfigProvider provider = ParamManager.getAppConfigProvider(input.getEnvironment(), input.getApp()); diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index a804e48eb..d45baf13d 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -12,14 +12,14 @@ <properties> <lambda.powertools.version>1.15.0</lambda.powertools.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <maven.compiler.source>11</maven.compiler.source> - <maven.compiler.target>11</maven.compiler.target> + <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.events>3.11.0</lambda.java.events> - <maven.shade.version>3.2.4</maven.shade.version> - <aspectj.version>1.13.1</aspectj.version> - <maven.compiler.version>3.10.1</maven.compiler.version> + <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> </properties> <modules> @@ -104,11 +104,6 @@ </dependency> </dependencies> </plugin> - <plugin> - <groupId>dev.aspectj</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> - </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> @@ -116,10 +111,100 @@ <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> + <useIncrementalCompilation>false</useIncrementalCompilation> </configuration> </plugin> </plugins> </pluginManagement> </build> + <profiles> + <profile> + <id>jdk8</id> + <activation> + <jdk>(,11)</jdk> <!-- < 11 --> + </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> + <activation> + <jdk>[11,)</jdk> <!-- >= 11 --> + </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> + </profile> + </profiles> + </project> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e497f568f..b6037054a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -14,9 +14,8 @@ <description>Powertools for AWS Lambda (Java)End-To-End Tests</description> <properties> - <!-- we can use Java 11 for integration tests, not forced to stick to Java 8 --> - <maven.compiler.source>8</maven.compiler.source> - <maven.compiler.target>8</maven.compiler.target> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.55</constructs.version> <cdk.version>2.84.0</cdk.version> 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 5abbb98df..dbebe721f 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 @@ -1,6 +1,5 @@ package software.amazon.lambda.powertools; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.*; import software.amazon.lambda.powertools.testutils.AppConfig; import software.amazon.lambda.powertools.testutils.Infrastructure; @@ -8,6 +7,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -17,19 +17,16 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ParametersE2ET { - - - private final ObjectMapper objectMapper = new ObjectMapper(); - private Infrastructure infrastructure; private String functionName; private final AppConfig appConfig; public ParametersE2ET() { + String appName = UUID.randomUUID().toString(); Map<String,String> params = new HashMap<>(); params.put("key1", "value1"); params.put("key2", "value2"); - appConfig = new AppConfig("e2eApp", "e2etest", params); + appConfig = new AppConfig(appName, "e2etest", params); } @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) From 458e8f68a0208cbd90ec37f41c255a8547fa8f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:04:10 +0200 Subject: [PATCH 0337/1008] removing logback (#1227) --- pom.xml | 6 ------ powertools-cloudformation/pom.xml | 5 ----- .../src/test/resources/logback.xml | 11 ---------- powertools-e2e-tests/pom.xml | 7 +++---- .../src/test/resources/log4j2.xml | 16 +++++++++++++++ .../src/test/resources/logback-test.xml | 20 ------------------- 6 files changed, 19 insertions(+), 46 deletions(-) delete mode 100644 powertools-cloudformation/src/test/resources/logback.xml create mode 100644 powertools-e2e-tests/src/test/resources/log4j2.xml delete mode 100644 powertools-e2e-tests/src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index dd2ba70ac..2bd0c21a4 100644 --- a/pom.xml +++ b/pom.xml @@ -293,12 +293,6 @@ <version>1.1.1</version> <scope>test</scope> </dependency> - <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - <version>1.3.8</version> - <scope>test</scope> - </dependency> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 2d8b8d489..d404df42d 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -98,11 +98,6 @@ <artifactId>wiremock-jre8</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - <scope>test</scope> - </dependency> </dependencies> </project> \ No newline at end of file diff --git a/powertools-cloudformation/src/test/resources/logback.xml b/powertools-cloudformation/src/test/resources/logback.xml deleted file mode 100644 index 8c752522e..000000000 --- a/powertools-cloudformation/src/test/resources/logback.xml +++ /dev/null @@ -1,11 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - <logger name="software.amazon.lambda.powertools" level="DEBUG" /> - <root level="INFO"> - <appender-ref ref="STDOUT" /> - </root> -</configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index b6037054a..52f3c2c26 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -23,12 +23,11 @@ <maven.deploy.skip>true</maven.deploy.skip> </properties> - <dependencies> <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - <version>1.3.8</version> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <scope>test</scope> </dependency> <dependency> diff --git a/powertools-e2e-tests/src/test/resources/log4j2.xml b/powertools-e2e-tests/src/test/resources/log4j2.xml new file mode 100644 index 000000000..12bd0b59d --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="INFO"> + <Appenders> + <Console name="Console" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" /> + </Console> + </Appenders> + <Loggers> + <Logger name="software.amazon.lambda.powertools" level="debug" additivity="false"> + <AppenderRef ref="Console"/> + </Logger> + <Root level="info"> + <AppenderRef ref="Console" /> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/powertools-e2e-tests/src/test/resources/logback-test.xml b/powertools-e2e-tests/src/test/resources/logback-test.xml deleted file mode 100644 index aac638007..000000000 --- a/powertools-e2e-tests/src/test/resources/logback-test.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<!DOCTYPE configuration> - -<configuration> - <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/> - <import class="ch.qos.logback.core.ConsoleAppender"/> - - <appender name="STDOUT" class="ConsoleAppender"> - <encoder class="PatternLayoutEncoder"> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="software.amazon.lambda.powertools" level="DEBUG"/> - - <root level="info"> - <appender-ref ref="STDOUT"/> - </root> - -</configuration> \ No newline at end of file From 21a2a1e0dfaab4d4c10f6b8de10cc08647b6230d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:10:43 +0200 Subject: [PATCH 0338/1008] Update auto_assign-issues.yml change people auto assigned (steve and pankaj are not there anymore) --- .github/auto_assign-issues.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/auto_assign-issues.yml b/.github/auto_assign-issues.yml index 652e18ad5..fb160ed94 100644 --- a/.github/auto_assign-issues.yml +++ b/.github/auto_assign-issues.yml @@ -3,6 +3,7 @@ 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 - - pankajagrawal16 - - stevehouel From 3aa48bfeaa994dafa330905d3e053bf84fce0ce2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:14:41 +0200 Subject: [PATCH 0339/1008] build(deps-dev): bump aws-cdk-lib from 2.84.0 to 2.85.0 (#1229) Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.84.0 to 2.85.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.84.0...v2.85.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 52f3c2c26..442b4c59c 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.55</constructs.version> - <cdk.version>2.84.0</cdk.version> + <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From da647dd3d6e5613b5bdab02fe630e234fad1425d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:20:53 +0200 Subject: [PATCH 0340/1008] build(deps-dev): bump constructs from 10.2.55 to 10.2.56 (#1230) Bumps [constructs](https://github.com/aws/constructs) from 10.2.55 to 10.2.56. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.55...v10.2.56) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 442b4c59c..fc0e30008 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.55</constructs.version> + <constructs.version>10.2.56</constructs.version> <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> From c0d9e41a092d77455c9ffd8c85ae4702d6b79317 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:24:25 +0200 Subject: [PATCH 0341/1008] build(deps): bump spotbugs-maven-plugin from 4.7.3.4 to 4.7.3.5 (#1220) Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.7.3.4 to 4.7.3.5. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2bd0c21a4..c48501761 100644 --- a/pom.xml +++ b/pom.xml @@ -458,7 +458,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.4</version> + <version>4.7.3.5</version> <executions> <execution> <id>test</id> From acf91bcf2d00e2091eed1ff4056762dcac1db42f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 14:36:45 +0200 Subject: [PATCH 0342/1008] build(deps): bump aws.sdk.version from 2.20.88 to 2.20.90 (#1228) Bumps `aws.sdk.version` from 2.20.88 to 2.20.90. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 44c84956d..a355e26ec 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.88</version> + <version>2.20.90</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index c48501761..ab6c8d266 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.88</aws.sdk.version> + <aws.sdk.version>2.20.90</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 7c09df4e4587f34f61a507ce80f61f94cbc0dcae Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Fri, 23 Jun 2023 16:28:00 +0100 Subject: [PATCH 0343/1008] chore: Update docs base origin url (#1238) * Update extra.js base_origin url * Update docs index.md * Update examples core link * Update AppConfigProvider.java link to parameters * Update template.yaml link * Update template.yaml fix url to java --- docs/index.md | 4 ++-- docs/javascript/extra.js | 4 ++-- examples/powertools-examples-core/template.yaml | 2 +- examples/powertools-examples-sqs/template.yaml | 2 +- .../lambda/powertools/parameters/AppConfigProvider.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/index.md b/docs/index.md index ee5429a3d..4358b08f4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ description: Powertools for AWS Lambda (Java) 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. ???+ tip - Powertools for AWS Lambda is also available for [Python](https://awslabs.github.io/aws-lambda-powertools-python/){target="_blank"}, [TypeScript](https://awslabs.github.io/aws-lambda-powertools-typescript/){target="_blank"}, and [.NET](https://awslabs.github.io/aws-lambda-powertools-dotnet/){target="_blank"} + 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?" @@ -142,4 +142,4 @@ For more information about the project and available options refer to this [repo | **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) | \ No newline at end of file +| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Enables/Disables tracing mode to capture method error | [Tracing](./core/tracing) | diff --git a/docs/javascript/extra.js b/docs/javascript/extra.js index 6e9193366..603c1ffc2 100644 --- a/docs/javascript/extra.js +++ b/docs/javascript/extra.js @@ -10,7 +10,7 @@ const awsconfig = { }; const RUNTIME = "java" -const BASE_ORIGIN = "awslabs.github.io" +const BASE_ORIGIN = "docs.powertools.aws.dev" function enableSearchOnBlurElement() { if (document.location.hostname != BASE_ORIGIN) return // prevent unnecessary data @@ -73,4 +73,4 @@ const recordPageView = ({prevLocation, searchPattern}) => { }, 'AWSKinesisFirehose') } -init() \ No newline at end of file +init() diff --git a/examples/powertools-examples-core/template.yaml b/examples/powertools-examples-core/template.yaml index 2bf3c3e98..9a51a1ba9 100644 --- a/examples/powertools-examples-core/template.yaml +++ b/examples/powertools-examples-core/template.yaml @@ -12,7 +12,7 @@ Globals: Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html Environment: Variables: - # Powertools for AWS Lambda (Java) env vars: https://awslabs.github.io/aws-lambda-powertools-python/#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 diff --git a/examples/powertools-examples-sqs/template.yaml b/examples/powertools-examples-sqs/template.yaml index 5264465f9..50327de18 100644 --- a/examples/powertools-examples-sqs/template.yaml +++ b/examples/powertools-examples-sqs/template.yaml @@ -11,7 +11,7 @@ Globals: Tracing: Active Environment: Variables: - # Powertools for AWS Lambda (Java) env vars: https://awslabs.github.io/aws-lambda-powertools-python/#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 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 index 90f30cb0e..6e664a82e 100644 --- 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 @@ -31,7 +31,7 @@ * value when the user re-requests it. This means we must hold a keyed set of session tokens * and values. * - * @see <a href="https://awslabs.github.io/aws-lambda-powertools-java/utilities/parameters">Parameters provider documentation</a> + * @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{ From 9adc126c5186f714367abe8f4efe4f6eebe19d5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Jun 2023 08:30:11 +0200 Subject: [PATCH 0344/1008] build(deps): bump aws.sdk.version from 2.20.90 to 2.20.91 (#1233) Bumps `aws.sdk.version` from 2.20.90 to 2.20.91. --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index a355e26ec..a4a45b878 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.90</version> + <version>2.20.91</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index ab6c8d266..963e5bf0b 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.90</aws.sdk.version> + <aws.sdk.version>2.20.91</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 1425deec6ba51d1a5a3b158119fcd1f71d4190b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Jun 2023 08:37:15 +0200 Subject: [PATCH 0345/1008] build(deps): bump json-schema-validator from 1.0.84 to 1.0.85 (#1235) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.84 to 1.0.85. --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index e9e035a65..ba70e33f4 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.84</version> + <version>1.0.85</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 967cb717dda5d602c14ee4ee7235d1d03473e421 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Jun 2023 08:38:09 +0200 Subject: [PATCH 0346/1008] build(deps-dev): bump constructs from 10.2.56 to 10.2.57 (#1234) Bumps [constructs](https://github.com/aws/constructs) from 10.2.56 to 10.2.57. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index fc0e30008..c551e36b3 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.56</constructs.version> + <constructs.version>10.2.57</constructs.version> <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> From c81d54eeca5b5c377306ec864a1f6fafad2a356d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:14:54 +0200 Subject: [PATCH 0347/1008] build(deps): bump aws.sdk.version from 2.20.91 to 2.20.92 (#1241) Bumps `aws.sdk.version` from 2.20.91 to 2.20.92. Updates `software.amazon.awssdk:bom` from 2.20.91 to 2.20.92 Updates `http-client-spi` from 2.20.91 to 2.20.92 Updates `url-connection-client` from 2.20.91 to 2.20.92 Updates `s3` from 2.20.91 to 2.20.92 Updates `lambda` from 2.20.91 to 2.20.92 Updates `cloudwatch` from 2.20.91 to 2.20.92 Updates `xray` from 2.20.91 to 2.20.92 Updates `cloudformation` from 2.20.91 to 2.20.92 Updates `sts` from 2.20.91 to 2.20.92 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index a4a45b878..52bde1167 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -25,7 +25,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.91</version> + <version>2.20.92</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 963e5bf0b..dcc5b74f3 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.91</aws.sdk.version> + <aws.sdk.version>2.20.92</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 0dac6fef670b23a238ae00be5795ff8688cfddd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:20:18 +0200 Subject: [PATCH 0348/1008] build(deps-dev): bump constructs from 10.2.57 to 10.2.60 (#1242) Bumps [constructs](https://github.com/aws/constructs) from 10.2.57 to 10.2.60. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.57...v10.2.60) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index c551e36b3..6614baf2a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.57</constructs.version> + <constructs.version>10.2.60</constructs.version> <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> From f30407ccf3a631ffd33d2bbfd81f3b4f044b3e2b Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 27 Jun 2023 09:24:38 +0200 Subject: [PATCH 0349/1008] [fix] Decouple samples from the parent pom (#1240) --- .github/workflows/build.yml | 2 +- examples/README.md | 3 +- examples/pom.xml | 36 +------- examples/powertools-examples-core/pom.xml | 91 +++++++++++++++++-- .../powertools-examples-idempotency/pom.xml | 88 +++++++++++++++++- .../powertools-examples-parameters/pom.xml | 77 ++++++++++++++-- .../powertools-examples-serialization/pom.xml | 76 +++++++++++++++- examples/powertools-examples-sqs/pom.xml | 83 ++++++++++++++++- .../powertools-examples-validation/pom.xml | 78 +++++++++++++++- 9 files changed, 465 insertions(+), 69 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38f99bd4e..a59e2fa71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ jobs: java-version: ${{ matrix.java }} cache: 'maven' - name: Build with Maven - run: mvn -Pbuild-without-spotbugs -B package --file pom.xml + run: mvn -Pbuild-without-spotbugs -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 diff --git a/examples/README.md b/examples/README.md index c853937e0..f7e6fc620 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,5 +1,4 @@ ## aws-lambda-powertools-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. \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 95de07209..ab61cf716 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -4,15 +4,11 @@ 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-examples</artifactId> + <version>1.15.0</version> <packaging>pom</packaging> - <parent> - <artifactId>powertools-parent</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> - <name>Powertools for AWS Lambda (Java) library Examples</name> <description> A suite of examples accompanying for Powertools for AWS Lambda (Java). @@ -32,32 +28,4 @@ <maven.deploy.skip>true</maven.deploy.skip> </properties> - <build> - <plugins> - <plugin> <!-- skip coverage for examples --> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <executions> - <execution> - <goals> - <goal>prepare-agent</goal> - </goals> - <configuration> - <skip>true</skip> - </configuration> - </execution> - <execution> - <id>report</id> - <goals> - <goal>report</goal> - </goals> - <configuration> - <skip>true</skip> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </project> \ No newline at end of file diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index e6ccd3918..9ce699c32 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -1,15 +1,19 @@ <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.15.0</version> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> - <parent> - <artifactId>powertools-examples</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> + <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> + </properties> <dependencies> <dependency> @@ -118,4 +122,79 @@ </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-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index c872ee905..a0e50c85c 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -1,15 +1,18 @@ <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.15.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> - <parent> - <artifactId>powertools-examples</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> + <properties> + <log4j.version>2.20.0</log4j.version> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> <dependencies> <dependency> @@ -168,6 +171,81 @@ </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> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index ac7d2176c..c2f6c1207 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -1,16 +1,17 @@ <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.15.0</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> - <parent> - <artifactId>powertools-examples</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> - + <properties> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> + <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> @@ -87,4 +88,68 @@ </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/pom.xml b/examples/powertools-examples-serialization/pom.xml index b3c7d70f7..371d652f0 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -1,15 +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"> <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>1.15.0</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> - <parent> - <artifactId>powertools-examples</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> + <properties> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> <dependencies> <dependency> @@ -64,4 +65,69 @@ </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-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 52bde1167..612a9423e 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -1,15 +1,17 @@ <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.15.0</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> - <parent> - <artifactId>powertools-examples</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> + <properties> + <log4j.version>2.20.0</log4j.version> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> <dependencies> <dependency> @@ -119,4 +121,75 @@ </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-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index f63a54b57..dbd6dee47 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -1,15 +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"> <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <version>1.15.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> - <parent> - <artifactId>powertools-examples</artifactId> - <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> - </parent> + <properties> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> <dependencies> <dependency> @@ -82,4 +83,71 @@ </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-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> </project> From 557e87502d925a8013caa36e3ae637f79c30c584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 27 Jun 2023 12:59:12 +0200 Subject: [PATCH 0350/1008] remove 'without-spotbugs' profile (#1236) --- .github/workflows/build.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/run-e2e-tests.yml | 2 +- pom.xml | 59 ++++++----------------------- 4 files changed, 15 insertions(+), 50 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a59e2fa71..5a4cc8daa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ jobs: java-version: ${{ matrix.java }} cache: 'maven' - name: Build with Maven - run: mvn -Pbuild-without-spotbugs -B install --file pom.xml + 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 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a51530d26..2068c09c5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,7 +24,7 @@ jobs: RELEASE_TAG_VERSION=${{ github.event.release.tag_name }} echo "RELEASE_TAG_VERSION=${RELEASE_TAG_VERSION:1}" >> $GITHUB_ENV - name: Publish package - run: mvn -P sign,build-without-spotbugs clean deploy -DskipTests + run: mvn -Prelease clean deploy -DskipTests env: MAVEN_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index bc5ffd437..4ed79ca80 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -45,4 +45,4 @@ jobs: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Run e2e test with Maven - run: mvn -Pbuild-without-spotbugs -DskipTests install --file pom.xml && mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file + 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/pom.xml b/pom.xml index dcc5b74f3..fef97d783 100644 --- a/pom.xml +++ b/pom.xml @@ -423,7 +423,7 @@ <profiles> <profile> - <id>sign</id> + <id>release</id> <build> <plugins> <plugin> @@ -445,33 +445,6 @@ </execution> </executions> </plugin> - </plugins> - </build> - </profile> - <profile> - <id>build-with-spotbugs</id> - <activation> - <activeByDefault>false</activeByDefault> - </activation> - <build> - <plugins> - <plugin> - <groupId>com.github.spotbugs</groupId> - <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.5</version> - <executions> - <execution> - <id>test</id> - <goals> - <goal>check</goal> - </goals> - </execution> - </executions> - <configuration> - <xmlOutput>true</xmlOutput> - <excludeFilterFile>../spotbugs-exclude.xml</excludeFilterFile> - </configuration> - </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> @@ -504,36 +477,28 @@ </build> </profile> <profile> - <id>build-without-spotbugs</id> + <id>build-with-spotbugs</id> + <activation> + <activeByDefault>false</activeByDefault> + </activation> <build> <plugins> <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-source-plugin</artifactId> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-maven-plugin</artifactId> + <version>4.7.3.5</version> <executions> <execution> - <id>attach-sources</id> + <id>test</id> <goals> - <goal>jar-no-fork</goal> + <goal>check</goal> </goals> </execution> </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> <configuration> - <doclint>none</doclint> - <detectJavaApiLink>false</detectJavaApiLink> + <xmlOutput>true</xmlOutput> + <excludeFilterFile>../spotbugs-exclude.xml</excludeFilterFile> </configuration> - <executions> - <execution> - <id>attach-javadocs</id> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> </plugin> </plugins> </build> From 3484546fea6904772bf6e80299ce093a476a7771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:11:49 +0200 Subject: [PATCH 0351/1008] build(deps): bump aws.sdk.version from 2.20.92 to 2.20.93 (#1244) Bumps `aws.sdk.version` from 2.20.92 to 2.20.93. Updates `software.amazon.awssdk:bom` from 2.20.92 to 2.20.93 Updates `http-client-spi` from 2.20.92 to 2.20.93 Updates `url-connection-client` from 2.20.92 to 2.20.93 Updates `s3` from 2.20.92 to 2.20.93 Updates `lambda` from 2.20.92 to 2.20.93 Updates `cloudwatch` from 2.20.92 to 2.20.93 Updates `xray` from 2.20.92 to 2.20.93 Updates `cloudformation` from 2.20.92 to 2.20.93 Updates `sts` from 2.20.92 to 2.20.93 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 612a9423e..981dd86ec 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.92</version> + <version>2.20.93</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index fef97d783..61cb798a7 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.92</aws.sdk.version> + <aws.sdk.version>2.20.93</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 9afd274272a54221dac36522d883893df98c5f19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:16:46 +0200 Subject: [PATCH 0352/1008] build(deps-dev): bump constructs from 10.2.60 to 10.2.61 (#1245) Bumps [constructs](https://github.com/aws/constructs) from 10.2.60 to 10.2.61. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.60...v10.2.61) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 6614baf2a..90d4eb205 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.60</constructs.version> + <constructs.version>10.2.61</constructs.version> <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> From f331c23279e9f715252df3444aebaa5582809dc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jun 2023 14:13:08 +0200 Subject: [PATCH 0353/1008] build(deps-dev): bump constructs from 10.2.61 to 10.2.62 (#1247) Bumps [constructs](https://github.com/aws/constructs) from 10.2.61 to 10.2.62. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.61...v10.2.62) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 90d4eb205..6a5e185b2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.61</constructs.version> + <constructs.version>10.2.62</constructs.version> <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> From fdd88ca0d21524beacaaeccc198d3842cdf371c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jun 2023 14:18:24 +0200 Subject: [PATCH 0354/1008] build(deps): bump aws.sdk.version from 2.20.93 to 2.20.94 (#1248) Bumps `aws.sdk.version` from 2.20.93 to 2.20.94. Updates `software.amazon.awssdk:bom` from 2.20.93 to 2.20.94 Updates `http-client-spi` from 2.20.93 to 2.20.94 Updates `url-connection-client` from 2.20.93 to 2.20.94 Updates `s3` from 2.20.93 to 2.20.94 Updates `lambda` from 2.20.93 to 2.20.94 Updates `cloudwatch` from 2.20.93 to 2.20.94 Updates `xray` from 2.20.93 to 2.20.94 Updates `cloudformation` from 2.20.93 to 2.20.94 Updates `sts` from 2.20.93 to 2.20.94 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 981dd86ec..b4ea54f99 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.93</version> + <version>2.20.94</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 61cb798a7..c44bf8980 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.93</aws.sdk.version> + <aws.sdk.version>2.20.94</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 01f7ea0a22bf1a93784e414c209091c4e3686263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:18:08 +0200 Subject: [PATCH 0355/1008] chore:Prep release 1.16.0 (#1249) * chore:prep release 1.16.0 * Write release notes --------- Co-authored-by: scottgerring <scottgerring@users.noreply.github.com> Co-authored-by: Scott Gerring <gerrings@amazon.com> --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-core/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 24 files changed, 42 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9055d2c5d..775074e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,23 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.16.0] - 2023-06-29 + + +### Added +* Feature: Add AppConfig provider to parameters module (#1104) by @scottgerring + +### 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. + + ## [1.15.0] - 2023-03-20 ### Added diff --git a/README.md b/README.md index a79272d36..c317760a1 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.15.0</version> + <version>1.16.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.15.0</version> + <version>1.16.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.15.0</version> + <version>1.16.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index ab61cf716..46801e0c6 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.15.0</version> + <version>1.16.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 9ce699c32..5755f295f 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.15.0</version> + <version>1.16.0</version> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index a0e50c85c..7a2b7d7a9 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.15.0</version> + <version>1.16.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index c2f6c1207..58cb43093 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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.15.0</version> + <version>1.16.0</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 371d652f0..6ad149e34 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.15.0</version> + <version>1.16.0</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index b4ea54f99..05a5b20be 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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.15.0</version> + <version>1.16.0</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index dbd6dee47..413d93f98 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -2,7 +2,7 @@ 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.15.0</version> + <version>1.16.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 69d9c57db..b950e5dcf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,7 +83,7 @@ extra_javascript: extra: powertools: - version: 1.15.0 + version: 1.16.0 repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index c44bf8980..370d987f3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.15.0</version> + <version>1.16.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index d404df42d..f561e56fb 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 6aa2414d1..cc6e19557 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index d45baf13d..c0ef40e5f 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.15.0</lambda.powertools.version> + <lambda.powertools.version>1.16.0</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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 6a5e185b2..0008705b0 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index a49a70c40..c053c5e9e 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index dc9d7a0dc..3d1477d16 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 345efe558..b204353b6 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index d1fb10183..a0e4bcaaf 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 2c507a425..9889922b7 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 035bbc636..317c8161d 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 7b5c396a6..c5a374781 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 434d2d048..d6453bf60 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index ba70e33f4..040f3ddde 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.15.0</version> + <version>1.16.0</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 3a158febb8a56864ed062fa4575777d2ea396d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 30 Jun 2023 10:02:30 +0200 Subject: [PATCH 0356/1008] update poms to next dev version: 1.17.0-SNAPSHOT (#1252) --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 23 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index c317760a1..fe6e9afba 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 46801e0c6..cca621163 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 5755f295f..4bcb931f6 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 7a2b7d7a9..0309df996 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 58cb43093..929e99f93 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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.0</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 6ad149e34..5db4e47c3 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.0</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 05a5b20be..8ad821959 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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.0</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 413d93f98..b427f7b21 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -2,7 +2,7 @@ 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.0</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index b950e5dcf..b758239de 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,7 +83,7 @@ extra_javascript: extra: powertools: - version: 1.16.0 + version: 1.17.0-SNAPSHOT repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index 370d987f3..5a182de0a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index f561e56fb..3a846c378 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index cc6e19557..f259b1c65 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index c0ef40e5f..ae26364dd 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.16.0</lambda.powertools.version> + <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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0008705b0..b5b875cec 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index c053c5e9e..cb4d9b802 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 3d1477d16..767fbd3ee 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index b204353b6..ed2d2f815 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index a0e4bcaaf..af980a4c3 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 9889922b7..f21ecb412 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 317c8161d..5eafcc8d3 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index c5a374781..5737296da 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d6453bf60..42a229f42 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 040f3ddde..7ab43831e 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.0</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From cca369188d8e0bc9f64f97e6bed15e63522115ee Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 30 Jun 2023 10:31:54 +0200 Subject: [PATCH 0357/1008] fix: examples shouldn't be deployed to mvn central (#1253) --- examples/powertools-examples-core/pom.xml | 1 + examples/powertools-examples-idempotency/pom.xml | 3 ++- examples/powertools-examples-parameters/pom.xml | 1 + examples/powertools-examples-serialization/pom.xml | 1 + examples/powertools-examples-sqs/pom.xml | 3 ++- examples/powertools-examples-validation/pom.xml | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index 4bcb931f6..a5d854d6b 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -13,6 +13,7 @@ <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> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 0309df996..0607fdd14 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -11,7 +11,8 @@ <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.compiler.target>1.8</maven.compiler.target> + <maven.deploy.skip>true</maven.deploy.skip> </properties> <dependencies> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 929e99f93..c4631fd05 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -10,6 +10,7 @@ <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> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 5db4e47c3..4974842dd 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -10,6 +10,7 @@ <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> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 8ad821959..ef2739ce4 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -10,7 +10,8 @@ <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.compiler.target>1.8</maven.compiler.target> + <maven.deploy.skip>true</maven.deploy.skip> </properties> <dependencies> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index b427f7b21..455fd66b8 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -10,6 +10,7 @@ <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> From cd8b00a70d4ccb9191ba9ca3f87418fd84476b6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:52:01 +0200 Subject: [PATCH 0358/1008] build(deps): bump aws.sdk.version from 2.20.94 to 2.20.96 (#1254) Bumps `aws.sdk.version` from 2.20.94 to 2.20.96. Updates `software.amazon.awssdk:bom` from 2.20.94 to 2.20.96 Updates `http-client-spi` from 2.20.94 to 2.20.96 Updates `url-connection-client` from 2.20.94 to 2.20.96 Updates `s3` from 2.20.94 to 2.20.96 Updates `lambda` from 2.20.94 to 2.20.96 Updates `cloudwatch` from 2.20.94 to 2.20.96 Updates `xray` from 2.20.94 to 2.20.96 Updates `cloudformation` from 2.20.94 to 2.20.96 Updates `sts` from 2.20.94 to 2.20.96 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index ef2739ce4..d989fba75 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.94</version> + <version>2.20.96</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 5a182de0a..af11f402b 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.94</aws.sdk.version> + <aws.sdk.version>2.20.96</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 7fc50ae6f185b00672dc46adc772738e9962219d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:09:26 +0200 Subject: [PATCH 0359/1008] build(deps-dev): bump constructs from 10.2.62 to 10.2.64 (#1257) Bumps [constructs](https://github.com/aws/constructs) from 10.2.62 to 10.2.64. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index b5b875cec..27d889981 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.62</constructs.version> + <constructs.version>10.2.64</constructs.version> <cdk.version>2.85.0</cdk.version> <!-- Don't deploy the e2e tests --> From 716a798a5207694ca934857b82279e7e94cc9b2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:15:42 +0200 Subject: [PATCH 0360/1008] build(deps-dev): bump aws-cdk-lib from 2.85.0 to 2.86.0 (#1256) Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.85.0 to 2.86.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.85.0...v2.86.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 27d889981..1e3e6a167 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.64</constructs.version> - <cdk.version>2.85.0</cdk.version> + <cdk.version>2.86.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From d8d13c006ac89740088078e98b14538d318471a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:30:59 +0200 Subject: [PATCH 0361/1008] build(deps): bump aws.sdk.version from 2.20.96 to 2.20.97 (#1260) Bumps `aws.sdk.version` from 2.20.96 to 2.20.97. Updates `software.amazon.awssdk:bom` from 2.20.96 to 2.20.97 Updates `http-client-spi` from 2.20.96 to 2.20.97 Updates `url-connection-client` from 2.20.96 to 2.20.97 Updates `s3` from 2.20.96 to 2.20.97 Updates `lambda` from 2.20.96 to 2.20.97 Updates `cloudwatch` from 2.20.96 to 2.20.97 Updates `xray` from 2.20.96 to 2.20.97 Updates `cloudformation` from 2.20.96 to 2.20.97 Updates `sts` from 2.20.96 to 2.20.97 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index d989fba75..1ee276d18 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.96</version> + <version>2.20.97</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index af11f402b..28118a33f 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.96</aws.sdk.version> + <aws.sdk.version>2.20.97</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 3e97bc803195a518746731fe8c80d2b0a2ea507a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:35:59 +0200 Subject: [PATCH 0362/1008] build(deps-dev): bump constructs from 10.2.64 to 10.2.67 (#1261) Bumps [constructs](https://github.com/aws/constructs) from 10.2.64 to 10.2.67. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.64...v10.2.67) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1e3e6a167..7ebeec5e0 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.64</constructs.version> + <constructs.version>10.2.67</constructs.version> <cdk.version>2.86.0</cdk.version> <!-- Don't deploy the e2e tests --> From f0dc2123e56b9af37544fbb6e8f26433d1b68d35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 13:48:26 +0200 Subject: [PATCH 0363/1008] build(deps): bump aws.sdk.version from 2.20.97 to 2.20.98 (#1262) Bumps `aws.sdk.version` from 2.20.97 to 2.20.98. Updates `software.amazon.awssdk:bom` from 2.20.97 to 2.20.98 Updates `http-client-spi` from 2.20.97 to 2.20.98 Updates `url-connection-client` from 2.20.97 to 2.20.98 Updates `s3` from 2.20.97 to 2.20.98 Updates `lambda` from 2.20.97 to 2.20.98 Updates `cloudwatch` from 2.20.97 to 2.20.98 Updates `xray` from 2.20.97 to 2.20.98 Updates `cloudformation` from 2.20.97 to 2.20.98 Updates `sts` from 2.20.97 to 2.20.98 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 1ee276d18..3b84b42b8 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.97</version> + <version>2.20.98</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 28118a33f..7c165e35c 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.97</aws.sdk.version> + <aws.sdk.version>2.20.98</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 49cb12f5e551f736b50a0efa34e222af072179c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 13:53:34 +0200 Subject: [PATCH 0364/1008] build(deps-dev): bump constructs from 10.2.67 to 10.2.68 (#1263) Bumps [constructs](https://github.com/aws/constructs) from 10.2.67 to 10.2.68. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.67...v10.2.68) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 7ebeec5e0..0d7a7ccda 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.67</constructs.version> + <constructs.version>10.2.68</constructs.version> <cdk.version>2.86.0</cdk.version> <!-- Don't deploy the e2e tests --> From d257989870692f6d6342b8d7edf0bd2b50aa95a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:50:54 +0200 Subject: [PATCH 0365/1008] build(deps-dev): bump constructs from 10.2.68 to 10.2.69 (#1265) Bumps [constructs](https://github.com/aws/constructs) from 10.2.68 to 10.2.69. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.68...v10.2.69) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0d7a7ccda..bf98b0691 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -16,7 +16,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.68</constructs.version> + <constructs.version>10.2.69</constructs.version> <cdk.version>2.86.0</cdk.version> <!-- Don't deploy the e2e tests --> From a6ec45870d7af17f1a8b81963623b776c222d3a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 14:00:09 +0200 Subject: [PATCH 0366/1008] build(deps): bump aws.sdk.version from 2.20.98 to 2.20.99 (#1266) Bumps `aws.sdk.version` from 2.20.98 to 2.20.99. Updates `software.amazon.awssdk:bom` from 2.20.98 to 2.20.99 Updates `http-client-spi` from 2.20.98 to 2.20.99 Updates `url-connection-client` from 2.20.98 to 2.20.99 Updates `s3` from 2.20.98 to 2.20.99 Updates `lambda` from 2.20.98 to 2.20.99 Updates `cloudwatch` from 2.20.98 to 2.20.99 Updates `xray` from 2.20.98 to 2.20.99 Updates `cloudformation` from 2.20.98 to 2.20.99 Updates `sts` from 2.20.98 to 2.20.99 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 3b84b42b8..5ecc17d47 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.98</version> + <version>2.20.99</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 7c165e35c..980d10aa7 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.98</aws.sdk.version> + <aws.sdk.version>2.20.99</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From f13dbc5666c6c1c228e4a11fd02723c081df6b73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 14:05:10 +0200 Subject: [PATCH 0367/1008] build(deps): bump json-schema-validator from 1.0.85 to 1.0.86 (#1267) Bumps [json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.85 to 1.0.86. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.85...1.0.86) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 7ab43831e..76c434614 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -73,7 +73,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.85</version> + <version>1.0.86</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 66cac4dbd6db6e968a2deb3de30e8d90ed05e024 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Jul 2023 14:06:06 +0200 Subject: [PATCH 0368/1008] build(deps-dev): bump aws-cdk-lib from 2.86.0 to 2.87.0 (#1270) Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.86.0 to 2.87.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.86.0...v2.87.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index bf98b0691..8754117db 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <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.86.0</cdk.version> + <cdk.version>2.87.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From 2c282a9353a199510fec903fd8d7667a0f4820d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:33:01 +0200 Subject: [PATCH 0369/1008] build(deps): bump aws.sdk.version from 2.20.99 to 2.20.101 (#1275) Bumps `aws.sdk.version` from 2.20.99 to 2.20.101. Updates `software.amazon.awssdk:bom` from 2.20.99 to 2.20.101 Updates `http-client-spi` from 2.20.99 to 2.20.101 Updates `url-connection-client` from 2.20.99 to 2.20.101 Updates `s3` from 2.20.99 to 2.20.101 Updates `lambda` from 2.20.99 to 2.20.101 Updates `cloudwatch` from 2.20.99 to 2.20.101 Updates `xray` from 2.20.99 to 2.20.101 Updates `cloudformation` from 2.20.99 to 2.20.101 Updates `sts` from 2.20.99 to 2.20.101 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 5ecc17d47..314a8c32b 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.99</version> + <version>2.20.101</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 980d10aa7..2e414d94f 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.99</aws.sdk.version> + <aws.sdk.version>2.20.101</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c7aedc4d2a61013450bf472bb7a8285f8cf80e41 Mon Sep 17 00:00:00 2001 From: Eleni Dimitropoulou <12170229+eldimi@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:57:06 +0300 Subject: [PATCH 0370/1008] chore(unit-test): Add missing unit tests in modules with low coverage (#1264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(unit-tests): add unit tests to powertools-core * chore(unit-tests): add unit tests to powertools-parameters * chore(unit-tests): add unit tests to powertools-serialization * chore(unit-tests): add unit tests to powertools-validation * chore(unit-tests): add unit tests to powertools-logging * chore(unit-tests): minor formatting fixes * chore(unit-tests): add missing return statement * chore(unit-test): rename, reformat, fix typos as suggested from code review Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * chore(unit-tests): address review comments * chore(unit-tests): revert refactoring of EventDeserializer * chore(unit-tests): expand and optimize imports * chore(unit-tests): change response of base64gzip when decompressing empty string * chore(unit-tests): add unit test and fix config for wrong arg type in base64gzip function invocation * chore(unit-tests): fix base64gzip function invocation for argument value argument --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- pom.xml | 1 + powertools-core/pom.xml | 5 + .../core/internal/LambdaConstants.java | 5 + .../core/internal/LambdaHandlerProcessor.java | 39 ++-- .../internal/LambdaHandlerProcessorTest.java | 203 ++++++++++++++++-- .../powertools/logging/LoggingUtils.java | 4 +- .../internal/AbstractJacksonLayoutCopy.java | 25 ++- .../logging/internal/LambdaLoggingAspect.java | 20 +- .../core/layout/LambdaJsonLayoutTest.java | 18 +- .../powertools/logging/LoggingUtilsTest.java | 6 +- .../handlers/PowerToolDisabledForStream.java | 6 +- ...erToolLogEventEnabledWithCustomMapper.java | 4 +- ...ava => PowertoolsLogAlbCorrelationId.java} | 6 +- ...> PowertoolsLogEnabledWithClearState.java} | 7 +- ...PowertoolsLogEventBridgeCorrelationId.java | 37 ++++ .../internal/LambdaLoggingAspectTest.java | 78 ++++--- .../parameters/AppConfigProvider.java | 2 - .../parameters/DynamoDbProvider.java | 6 +- .../powertools/parameters/ParamManager.java | 7 +- .../powertools/parameters/SSMProvider.java | 19 +- .../parameters/SecretsProvider.java | 19 +- .../internal/LambdaParametersAspect.java | 7 +- .../transform/Base64Transformer.java | 4 +- .../parameters/AppConfigProviderTest.java | 107 +++++++-- .../parameters/DynamoDbProviderTest.java | 66 +++++- .../ParamManagerIntegrationTest.java | 10 +- .../parameters/ParamManagerTest.java | 101 +++++++++ .../parameters/SSMProviderTest.java | 31 ++- .../parameters/SecretsProviderTest.java | 29 +++ .../internal/LambdaParametersAspectTest.java | 5 +- .../transform/TransformationManagerTest.java | 20 ++ .../utilities/EventDeserializer.java | 16 +- .../powertools/utilities/JsonConfig.java | 1 + .../jmespath/Base64GZipFunction.java | 26 ++- .../utilities/EventDeserializerTest.java | 121 ++++++++++- .../jmespath/Base64GZipFunctionTest.java | 45 ++++ .../powertools/utilities/model/Order.java | 33 +++ .../src/test/resources/alb_event.json | 28 +++ .../src/test/resources/amq_event.json | 29 +++ .../src/test/resources/apigwv2_event.json | 57 +++++ .../src/test/resources/cfcr_event.json | 20 ++ .../src/test/resources/custom_event.json | 2 +- .../src/test/resources/custom_event_gzip.json | 5 +- .../src/test/resources/custom_event_map.json | 9 + .../src/test/resources/cwl_event.json | 5 + .../src/test/resources/kafip_event.json | 14 ++ .../src/test/resources/kasip_event.json | 17 ++ .../src/test/resources/kf_event.json | 12 ++ .../src/test/resources/rabbitmq_event.json | 51 +++++ .../src/test/resources/scheduled_event.json | 12 ++ .../sqs/SqsUtilsBatchProcessorTest.java | 1 - powertools-validation/pom.xml | 9 +- .../validation/ValidationConfig.java | 22 +- .../validation/ValidationUtils.java | 30 ++- .../validation/internal/ValidationAspect.java | 21 +- .../validation/ValidationUtilsTest.java | 69 ++++-- ...ndler.java => GenericSchemaV7Handler.java} | 7 +- .../validation/handlers/KinesisHandler.java | 28 --- .../ValidationInboundClasspathHandler.java | 29 --- .../ValidationInboundStringHandler.java | 2 +- .../ResponseEventsArgumentsProvider.java | 57 +++++ .../internal/ValidationAspectTest.java | 109 +++++++++- .../powertools/validation/model/Basket.java | 6 +- .../src/test/resources/alb_event.json | 28 +++ .../src/test/resources/amq_event.json | 28 +++ .../src/test/resources/cfcr_event.json | 20 ++ .../src/test/resources/custom_event.json | 2 +- .../src/test/resources/custom_event_gzip.json | 2 +- .../src/test/resources/cwl_event.json | 5 + .../src/test/resources/kafip_event.json | 14 ++ .../src/test/resources/kafka_event.json | 27 +++ .../src/test/resources/kasip_event.json | 17 ++ .../src/test/resources/kf_event.json | 12 ++ .../src/test/resources/rabbitmq_event.json | 51 +++++ .../src/test/resources/scheduled_event.json | 14 ++ .../src/test/resources/schema_v4.json | 6 +- .../src/test/resources/sns_event.json | 26 +++ .../src/test/resources/sqs.json | 1 - .../src/test/resources/sqs_message.json | 1 - 79 files changed, 1728 insertions(+), 316 deletions(-) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolAlbCorrelationId.java => PowertoolsLogAlbCorrelationId.java} (78%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolEnabledWithClearState.java => PowertoolsLogEnabledWithClearState.java} (85%) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java create mode 100644 powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java create mode 100644 powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java create mode 100644 powertools-serialization/src/test/resources/alb_event.json create mode 100644 powertools-serialization/src/test/resources/amq_event.json create mode 100644 powertools-serialization/src/test/resources/apigwv2_event.json create mode 100644 powertools-serialization/src/test/resources/cfcr_event.json create mode 100644 powertools-serialization/src/test/resources/custom_event_map.json create mode 100644 powertools-serialization/src/test/resources/cwl_event.json create mode 100644 powertools-serialization/src/test/resources/kafip_event.json create mode 100644 powertools-serialization/src/test/resources/kasip_event.json create mode 100644 powertools-serialization/src/test/resources/kf_event.json create mode 100644 powertools-serialization/src/test/resources/rabbitmq_event.json create mode 100644 powertools-serialization/src/test/resources/scheduled_event.json rename powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/{SQSHandler.java => GenericSchemaV7Handler.java} (82%) delete mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandler.java delete mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundClasspathHandler.java create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java create mode 100644 powertools-validation/src/test/resources/alb_event.json create mode 100644 powertools-validation/src/test/resources/amq_event.json create mode 100644 powertools-validation/src/test/resources/cfcr_event.json create mode 100644 powertools-validation/src/test/resources/cwl_event.json create mode 100644 powertools-validation/src/test/resources/kafip_event.json create mode 100644 powertools-validation/src/test/resources/kafka_event.json create mode 100644 powertools-validation/src/test/resources/kasip_event.json create mode 100644 powertools-validation/src/test/resources/kf_event.json create mode 100644 powertools-validation/src/test/resources/rabbitmq_event.json create mode 100644 powertools-validation/src/test/resources/scheduled_event.json create mode 100644 powertools-validation/src/test/resources/sns_event.json diff --git a/pom.xml b/pom.xml index 2e414d94f..b7aaa1e90 100644 --- a/pom.xml +++ b/pom.xml @@ -516,6 +516,7 @@ <version>3.1.2</version> <configuration> <argLine> + @{argLine} --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED </argLine> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index f259b1c65..cf9ad45d1 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -82,6 +82,11 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-inline</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java index fe30b4928..bb5fc4666 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java @@ -18,4 +18,9 @@ public class LambdaConstants { public static final String AWS_REGION_ENV = "AWS_REGION"; public static final String AWS_LAMBDA_INITIALIZATION_TYPE = "AWS_LAMBDA_INITIALIZATION_TYPE"; public static final String ON_DEMAND = "on-demand"; + public static final String X_AMZN_TRACE_ID = "_X_AMZN_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"; } diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index 1cff812b8..c7f8b119f 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -27,15 +27,21 @@ import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; public final class LambdaHandlerProcessor { + // SERVICE_NAME cannot be final for testing purposes - private static String SERVICE_NAME = null != System.getenv("POWERTOOLS_SERVICE_NAME") - ? System.getenv("POWERTOOLS_SERVICE_NAME") : "service_undefined"; + private static String SERVICE_NAME = calculateServiceName(); + private static Boolean IS_COLD_START = null; private LambdaHandlerProcessor() { // Hide default constructor } + private static String calculateServiceName() { + return null != getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME) + ? getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME) : LambdaConstants.SERVICE_UNDEFINED; + } + public static boolean isHandlerMethod(final ProceedingJoinPoint pjp) { return placedOnRequestHandler(pjp) || placedOnStreamHandler(pjp); } @@ -56,23 +62,24 @@ public static boolean placedOnStreamHandler(final ProceedingJoinPoint pjp) { public static Context extractContext(final ProceedingJoinPoint pjp) { - if (isHandlerMethod(pjp)) { - if (placedOnRequestHandler(pjp)) { - return (Context) pjp.getArgs()[1]; - } - - if (placedOnStreamHandler(pjp)) { - return (Context) pjp.getArgs()[2]; - } + if (placedOnRequestHandler(pjp)) { + return (Context) pjp.getArgs()[1]; + } else if (placedOnStreamHandler(pjp)) { + return (Context) pjp.getArgs()[2]; + } else { + return null; } - - return null; } public static String serviceName() { return SERVICE_NAME; } + // Method used for testing purposes + protected static void resetServiceName() { + SERVICE_NAME = calculateServiceName(); + } + public static boolean isColdStart() { return IS_COLD_START == null; } @@ -82,13 +89,13 @@ public static void coldStartDone() { } public static boolean isSamLocal() { - return "true".equals(System.getenv("AWS_SAM_LOCAL")); + return "true".equals(getenv(LambdaConstants.AWS_SAM_LOCAL)); } public static Optional<String> getXrayTraceId() { - final String X_AMZN_TRACE_ID = getenv("_X_AMZN_TRACE_ID"); - if(X_AMZN_TRACE_ID != null) { - return of(X_AMZN_TRACE_ID.split(";")[0].replace("Root=", "")); + 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, "")); } return empty(); } diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java index 6ed8b4160..94ad97506 100644 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java +++ b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java @@ -6,60 +6,231 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import java.io.InputStream; import java.io.OutputStream; +import java.util.Optional; 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; class LambdaHandlerProcessorTest { + private Signature signature = mock(Signature.class); + private ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + @Test void isHandlerMethod_shouldRecognizeRequestHandler() { - ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(); + Object[] args = {new Object(), mock(Context.class)}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); } @Test void isHandlerMethod_shouldRecognizeRequestStreamHandler() { - ProceedingJoinPoint pjpMock = mockRequestStreamHandlerPjp(); + Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); } + @Test + void isHandlerMethod_shouldReturnFalse() { + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, new Object[]{}); + + boolean isHandlerMethod = LambdaHandlerProcessor.isHandlerMethod(pjpMock); + + assertThat(isHandlerMethod).isFalse(); + } + @Test void placedOnRequestHandler_shouldRecognizeRequestHandler() { - ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(); + Object[] args = {new Object(), mock(Context.class)}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); assertThat(LambdaHandlerProcessor.placedOnRequestHandler(pjpMock)).isTrue(); } @Test void placedOnStreamHandler_shouldRecognizeRequestStreamHandler() { - ProceedingJoinPoint pjpMock = mockRequestStreamHandlerPjp(); + Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); assertThat(LambdaHandlerProcessor.placedOnStreamHandler(pjpMock)).isTrue(); } - private static ProceedingJoinPoint mockRequestHandlerPjp() { - Signature signature = mock(Signature.class); - when(signature.getDeclaringType()).thenReturn(RequestHandler.class); - ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + @Test + void placedOnRequestHandler_shouldInvalidateOnWrongNoOfArgs() { + Object[] args = {new Object()}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); + + boolean isPlacedOnRequestHandler = LambdaHandlerProcessor.placedOnRequestHandler(pjpMock); + + assertThat(isPlacedOnRequestHandler).isFalse(); + } + + @Test + void placedOnRequestHandler_shouldInvalidateOnWrongTypeOfArgs() { + Object[] args = {new Object(), new Object()}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); + + boolean isPlacedOnRequestHandler = LambdaHandlerProcessor.placedOnRequestHandler(pjpMock); + + assertThat(isPlacedOnRequestHandler).isFalse(); + } + + @Test + void placedOnStreamHandler_shouldInvalidateOnWrongNoOfArgs() { + Object[] args = {new Object()}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); + + boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); + + assertThat(isPlacedOnStreamHandler).isFalse(); + } + + @Test + void placedOnStreamHandler_shouldInvalidateOnWrongTypeOfArgs() { + Object[] args = {new Object(), new Object(), new Object()}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); + + boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); + + assertThat(isPlacedOnStreamHandler).isFalse(); + } + + @Test + void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidOutputStreamArg() { + Object[] args = {mock(InputStream.class), new Object(), mock(Context.class)}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); + + boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); + + assertThat(isPlacedOnStreamHandler).isFalse(); + } + + @Test + void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidContextArg() { + Object[] args = {mock(InputStream.class), mock(OutputStream.class), new Object()}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); + + boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); + + assertThat(isPlacedOnStreamHandler).isFalse(); + } + + @Test + 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(); + + assertThat(xRayTraceId.isPresent()).isTrue(); + assertThat(traceID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")).isEqualTo(xRayTraceId.get()); + } + } + + @Test + 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(); + + assertThat(isXRayTraceIdPresent).isFalse(); + } + } + + @Test + void extractContext_fromRequestHandler() { Object[] args = {new Object(), mock(Context.class)}; - when(pjpMock.getArgs()).thenReturn(args); - when(pjpMock.getSignature()).thenReturn(signature); - return pjpMock; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestHandler.class, args); + + Context context = LambdaHandlerProcessor.extractContext(pjpMock); + + assertThat(context).isNotNull(); } - private static ProceedingJoinPoint mockRequestStreamHandlerPjp() { - Signature signature = mock(Signature.class); - when(signature.getDeclaringType()).thenReturn(RequestStreamHandler.class); - ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); + @Test + void extractContext_fromStreamRequestHandler() { Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; - when(pjpMock.getArgs()).thenReturn(args); + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); + + Context context = LambdaHandlerProcessor.extractContext(pjpMock); + + assertNotNull(context); + } + + @Test + void extractContext_notKnownHandler() { + Object[] args = {new Object()}; + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, args); + + Context context = LambdaHandlerProcessor.extractContext(pjpMock); + + assertThat(context).isNull(); + } + + @Test + void isColdStart() { + boolean isColdStart = LambdaHandlerProcessor.isColdStart(); + + assertThat(isColdStart).isTrue(); + } + + @Test + void isColdStart_coldStartDone() { + LambdaHandlerProcessor.coldStartDone(); + + boolean isColdStart = LambdaHandlerProcessor.isColdStart(); + + assertThat(isColdStart).isFalse(); + } + + @Test + void isSamLocal() { + try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { + mockedSystemWrapper.when(() -> getenv(LambdaConstants.AWS_SAM_LOCAL)).thenReturn("true"); + + boolean isSamLocal = LambdaHandlerProcessor.isSamLocal(); + + assertThat(isSamLocal).isTrue(); + } + } + + @Test + void serviceName() { + try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { + String expectedServiceName = "MyService"; + mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)).thenReturn(expectedServiceName); + + String actualServiceName = LambdaHandlerProcessor.serviceName(); + + assertThat(actualServiceName).isEqualTo(expectedServiceName); + } + } + + @Test + 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); + } + } + + private ProceedingJoinPoint mockRequestHandlerPjp(Class handlerClass, Object[] handlerArgs) { + when(signature.getDeclaringType()).thenReturn(handlerClass); + when(pjpMock.getArgs()).thenReturn(handlerArgs); when(pjpMock.getSignature()).thenReturn(signature); return pjpMock; } 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 index f23e274d4..9a1567d57 100644 --- 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 @@ -13,11 +13,11 @@ */ package software.amazon.lambda.powertools.logging; -import java.util.Map; - import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.logging.log4j.ThreadContext; +import java.util.Map; + import static java.util.Arrays.asList; /** 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 index 3ceda4b79..c96d1383e 100644 --- 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 @@ -1,11 +1,12 @@ package software.amazon.lambda.powertools.logging.internal; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.LinkedHashMap; -import java.util.Map; - +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 org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; @@ -25,13 +26,11 @@ import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.Strings; -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; @Deprecated abstract class AbstractJacksonLayoutCopy extends AbstractStringLayout { 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 f522a4711..d489e093b 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 @@ -13,16 +13,6 @@ */ package software.amazon.lambda.powertools.logging.internal; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.Map; -import java.util.Optional; -import java.util.Random; - import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.core.JsonProcessingException; @@ -42,6 +32,16 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Map; +import java.util.Optional; +import java.util.Random; + import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Optional.empty; import static java.util.Optional.ofNullable; 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 index 5fc0398d1..60f0806a9 100644 --- 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 @@ -13,15 +13,6 @@ */ package org.apache.logging.log4j.core.layout; -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 com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.fasterxml.jackson.core.JsonProcessingException; @@ -34,6 +25,15 @@ import software.amazon.lambda.powertools.logging.handlers.PowerLogToolSamplingEnabled; import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; +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 static java.util.Collections.emptyMap; import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; import static org.assertj.core.api.Assertions.assertThat; 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 index 91fea4c7a..eee8ace05 100644 --- 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 @@ -13,13 +13,13 @@ */ package software.amazon.lambda.powertools.logging; -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; +import java.util.HashMap; +import java.util.Map; + import static org.assertj.core.api.Assertions.assertThat; 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/PowerToolDisabledForStream.java index f0f7f676e..15b39c6c5 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/PowerToolDisabledForStream.java @@ -13,12 +13,12 @@ */ package software.amazon.lambda.powertools.logging.handlers; -import java.io.InputStream; -import java.io.OutputStream; - import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.InputStream; +import java.io.OutputStream; + public class PowerToolDisabledForStream implements RequestStreamHandler { @Override 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 index d761c9ac0..f1c2f62c8 100644 --- 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 @@ -1,7 +1,5 @@ package software.amazon.lambda.powertools.logging.handlers; -import java.io.IOException; - import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; @@ -13,6 +11,8 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; +import java.io.IOException; + public class PowerToolLogEventEnabledWithCustomMapper implements RequestHandler<S3EventNotification, Object> { static { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolAlbCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java similarity index 78% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolAlbCorrelationId.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java index 125c13e26..c06f8326e 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolAlbCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java @@ -18,14 +18,12 @@ import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.logging.CorrelationIdPathConstants; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_REST; import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.APPLICATION_LOAD_BALANCER; -public class PowerLogToolAlbCorrelationId implements RequestHandler<ApplicationLoadBalancerRequestEvent, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolAlbCorrelationId.class); +public class PowertoolsLogAlbCorrelationId implements RequestHandler<ApplicationLoadBalancerRequestEvent, Object> { + private final Logger LOG = LogManager.getLogger(PowertoolsLogAlbCorrelationId.class); @Override @Logging(correlationIdPath = APPLICATION_LOAD_BALANCER) diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledWithClearState.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java similarity index 85% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledWithClearState.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java index 8fef32c94..8b7d4fcaa 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledWithClearState.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java @@ -20,13 +20,14 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; -public class PowerLogToolEnabledWithClearState implements RequestHandler<Object, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolEnabledWithClearState.class); +public class PowertoolsLogEnabledWithClearState implements RequestHandler<Object, Object> { public static int COUNT = 1; + private static final Logger LOG = LogManager.getLogger(PowertoolsLogEnabledWithClearState.class); + @Override @Logging(clearState = true) public Object handleRequest(Object input, Context context) { - if(COUNT == 1) { + if (COUNT == 1) { LoggingUtils.appendKey("TestKey", "TestValue"); } LOG.info("Test event"); 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 new file mode 100644 index 000000000..f03983aff --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.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 com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.EVENT_BRIDGE; + +public class PowertoolsLogEventBridgeCorrelationId implements RequestStreamHandler { + + private final Logger LOG = LogManager.getLogger(PowertoolsLogEventBridgeCorrelationId.class); + + @Override + @Logging(correlationIdPath = EVENT_BRIDGE) + 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/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index e2cb58453..8e7d2d3e6 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 @@ -13,22 +13,6 @@ */ package software.amazon.lambda.powertools.logging.internal; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -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.Paths; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -49,25 +33,35 @@ 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.PowerLogToolAlbCorrelationId; 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.PowerLogToolEnabledWithClearState; 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 software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledWithClearState; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +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.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.RequestParametersEntity; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.ResponseElementsEntity; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3BucketEntity; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3Entity; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3ObjectEntity; -import static com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.UserIdentityEntity; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.joining; @@ -284,7 +278,7 @@ void shouldLogCorrelationIdOnAPIGatewayV2HTTPEvent(APIGatewayV2HTTPEvent event) @ParameterizedTest @Event(value = "albEvent.json", type = ApplicationLoadBalancerRequestEvent.class) void shouldLogCorrelationIdOnALBEvent(ApplicationLoadBalancerRequestEvent event) { - RequestHandler<ApplicationLoadBalancerRequestEvent, Object> handler = new PowerLogToolAlbCorrelationId(); + RequestHandler<ApplicationLoadBalancerRequestEvent, Object> handler = new PowertoolsLogAlbCorrelationId(); handler.handleRequest(event, context); assertThat(ThreadContext.getImmutableContext()) @@ -292,9 +286,23 @@ void shouldLogCorrelationIdOnALBEvent(ApplicationLoadBalancerRequestEvent event) .containsEntry("correlation_id", event.getHeaders().get("x-amzn-trace-id")); } + @Test + void shouldLogCorrelationIdOnStreamHandler() throws IOException { + RequestStreamHandler handler = new PowertoolsLogEventBridgeCorrelationId(); + String eventId = "3"; + String event = "{\"id\":" + eventId + "}"; // CorrelationIdPathConstants.EVENT_BRIDGE + ByteArrayInputStream inputStream = new ByteArrayInputStream(event.getBytes()); + handler.handleRequest(inputStream, new ByteArrayOutputStream(), context); + + + assertThat(ThreadContext.getImmutableContext()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry("correlation_id", eventId); + } + @Test void shouldLogAndClearLogContextOnEachRequest() throws IOException { - requestHandler = new PowerLogToolEnabledWithClearState(); + requestHandler = new PowertoolsLogEnabledWithClearState(); S3EventNotification s3EventNotification = s3EventNotification(); requestHandler.handleRequest(s3EventNotification, context); @@ -328,24 +336,24 @@ private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAcc } private S3EventNotification s3EventNotification() { - S3EventNotificationRecord record = new S3EventNotificationRecord("us-west-2", + S3EventNotification.S3EventNotificationRecord record = new S3EventNotification.S3EventNotificationRecord("us-west-2", "ObjectCreated:Put", "aws:s3", null, "2.1", - new RequestParametersEntity("127.0.0.1"), - new ResponseElementsEntity("C3D13FE58DE4C810", "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), - new S3Entity("testConfigRule", - new S3BucketEntity("mybucket", - new UserIdentityEntity("A3NL1KOZZKExample"), + 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 S3ObjectEntity("HappyFace.jpg", + new S3EventNotification.S3ObjectEntity("HappyFace.jpg", 1024L, "d41d8cd98f00b204e9800998ecf8427e", "096fKKXTRTtl3on89fVO.nfljtsv6qko", "0055AED6DCD90281E5"), "1.0"), - new UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") + new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") ); return new S3EventNotification(singletonList(record)); 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 index 6e664a82e..e0255125d 100644 --- 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 @@ -9,8 +9,6 @@ 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.awssdk.services.ssm.SsmClient; -import software.amazon.awssdk.services.ssm.SsmClientBuilder; import software.amazon.lambda.powertools.core.internal.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; 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 index bf8d763b9..5144af0c2 100644 --- 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 @@ -5,7 +5,11 @@ 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.*; +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.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; 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 index 96cbabd0e..b2e541c43 100644 --- 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 @@ -14,14 +14,13 @@ package software.amazon.lambda.powertools.parameters; 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.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.util.concurrent.ConcurrentHashMap; /** @@ -164,13 +163,13 @@ public static TransformationManager getTransformationManager() { return transformationManager; } - private static <T extends BaseProvider> T createProvider(Class<T> providerClass) { + static <T extends BaseProvider> T createProvider(Class<T> providerClass) { try { Constructor<T> constructor = providerClass.getDeclaredConstructor(CacheManager.class); T provider = constructor.newInstance(cacheManager); provider.setTransformationManager(transformationManager); return provider; - } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + } 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/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index bf36aa717..d6d87747b 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -13,10 +13,6 @@ */ package software.amazon.lambda.powertools.parameters; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.Map; - import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -32,6 +28,10 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; + import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; /** @@ -74,7 +74,7 @@ */ public class SSMProvider extends BaseProvider { - private final SsmClient client; + private SsmClient client; private boolean decrypt = false; private boolean recursive = false; @@ -92,6 +92,15 @@ public class SSMProvider extends BaseProvider { this.client = client; } + /** + * Constructor + * + * @param cacheManager handles the parameter caching + */ + SSMProvider(CacheManager cacheManager) { + super(cacheManager); + } + /** * Retrieve the parameter value from the AWS System Manager Parameter Store. * 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 index 9764564a9..54d0daee3 100644 --- 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 @@ -13,10 +13,6 @@ */ package software.amazon.lambda.powertools.parameters; -import java.time.temporal.ChronoUnit; -import java.util.Base64; -import java.util.Map; - import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -29,6 +25,10 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; +import java.time.temporal.ChronoUnit; +import java.util.Base64; +import java.util.Map; + import static java.nio.charset.StandardCharsets.UTF_8; import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; @@ -58,7 +58,7 @@ */ public class SecretsProvider extends BaseProvider { - private final SecretsManagerClient client; + private SecretsManagerClient client; /** * Constructor with custom {@link SecretsManagerClient}. <br/> @@ -73,6 +73,15 @@ public class SecretsProvider extends BaseProvider { this.client = client; } + /** + * Constructor + * + * @param cacheManager handles the parameter caching + */ + SecretsProvider(CacheManager cacheManager) { + super(cacheManager); + } + /** * Retrieve the parameter value from the AWS Secrets Manager. * 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 index ea4d465cd..8de2f3f57 100644 --- 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 @@ -5,7 +5,9 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.FieldSignature; -import software.amazon.lambda.powertools.parameters.*; +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 { @@ -16,9 +18,6 @@ public void getParam(Param paramAnnotation) { @Around("getParam(paramAnnotation)") public Object injectParam(final ProceedingJoinPoint joinPoint, final Param paramAnnotation) { - if(null == paramAnnotation.provider()) { - throw new IllegalArgumentException("provider for Param annotation cannot be null!"); - } BaseProvider provider = ParamManager.getProvider(paramAnnotation.provider()); if(paramAnnotation.transformer().isInterface()) { diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java index 944f4f03c..c666edce7 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java @@ -13,10 +13,10 @@ */ package software.amazon.lambda.powertools.parameters.transform; -import java.util.Base64; - import software.amazon.lambda.powertools.parameters.exception.TransformationException; +import java.util.Base64; + import static java.nio.charset.StandardCharsets.UTF_8; /** diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java index d72a1f042..23f6271da 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java @@ -7,7 +7,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest; import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationResponse; @@ -17,34 +16,30 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Duration; -import java.util.Optional; - -import static org.mockito.ArgumentMatchers.eq; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.mockito.MockitoAnnotations.openMocks; public class AppConfigProviderTest { + private final String environmentName = "test"; + private final String applicationName = "fakeApp"; + private final String defaultTestKey = "key1"; + @Mock AppConfigDataClient client; - - private AppConfigProvider provider; - + @Captor ArgumentCaptor<StartConfigurationSessionRequest> startSessionRequestCaptor; - + @Captor ArgumentCaptor<GetLatestConfigurationRequest> getLatestConfigurationRequestCaptor; - private final String environmentName = "test"; - - private final String applicationName = "fakeApp"; - - private final String defaultTestKey = "key1"; + private AppConfigProvider provider; @BeforeEach public void init() { openMocks(this); + provider = AppConfigProvider.builder() .withClient(client) .withApplication(applicationName) @@ -69,18 +64,18 @@ public void getValueRetrievesValue() { .build(); // first response returns 'value1' GetLatestConfigurationResponse firstResponse = GetLatestConfigurationResponse.builder() - .nextPollConfigurationToken("token2") - .configuration(SdkBytes.fromUtf8String("value1")) - .build(); + .nextPollConfigurationToken("token2") + .configuration(SdkBytes.fromUtf8String("value1")) + .build(); // Second response returns 'value2' GetLatestConfigurationResponse secondResponse = GetLatestConfigurationResponse.builder() - .nextPollConfigurationToken("token3") - .configuration(SdkBytes.fromUtf8String("value2")) - .build(); + .nextPollConfigurationToken("token3") + .configuration(SdkBytes.fromUtf8String("value2")) + .build(); // Third response returns nothing, which means the provider should yield the previous value again GetLatestConfigurationResponse thirdResponse = GetLatestConfigurationResponse.builder() - .nextPollConfigurationToken("token4") - .build(); + .nextPollConfigurationToken("token4") + .build(); Mockito.when(client.startConfigurationSession(startSessionRequestCaptor.capture())) .thenReturn(firstSession); Mockito.when(client.getLatestConfiguration(getLatestConfigurationRequestCaptor.capture())) @@ -103,6 +98,29 @@ public void getValueRetrievesValue() { assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(2).configurationToken()).isEqualTo(secondResponse.nextPollConfigurationToken()); } + @Test + public void getValueNoValueExists() { + + // Arrange + StartConfigurationSessionResponse session = StartConfigurationSessionResponse.builder() + .initialConfigurationToken("token1") + .build(); + GetLatestConfigurationResponse response = GetLatestConfigurationResponse.builder() + .nextPollConfigurationToken("token2") + .build(); + Mockito.when(client.startConfigurationSession(startSessionRequestCaptor.capture())) + .thenReturn(session); + Mockito.when(client.getLatestConfiguration(getLatestConfigurationRequestCaptor.capture())) + .thenReturn(response); + + // Act + String returnedValue = provider.getValue(defaultTestKey); + + + // Assert + assertThat(returnedValue).isEqualTo(null); + } + /** * If we mix requests for different keys together through the same provider, retrieval should * work as expected. This means two separate configuration sessions should be established with AppConfig. @@ -145,4 +163,47 @@ public void multipleKeysRetrievalWorks() { } + @Test + public 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() { + + // Act & Assert + assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() + .withEnvironment(environmentName) + .withApplication(applicationName) + .withClient(client) + .build()) + .withMessage("No CacheManager provided; please provide one"); + } + + @Test + public void testAppConfigProviderBuilderMissingEnvironment_throwsException() { + + // Act & Assert + assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() + .withCacheManager(new CacheManager()) + .withApplication(applicationName) + .withClient(client) + .build()) + .withMessage("No environment 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"); + } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java index 0e5b734d6..d6818a64f 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java @@ -8,15 +8,21 @@ import org.mockito.Mock; import org.mockito.Mockito; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.*; +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.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import java.util.HashMap; import java.util.Map; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.MockitoAnnotations.openMocks; public class DynamoDbProviderTest { @@ -24,6 +30,9 @@ public class DynamoDbProviderTest { @Mock DynamoDbClient client; + @Mock + TransformationManager transformationManager; + @Captor ArgumentCaptor<GetItemRequest> getItemValueCaptor; @@ -67,7 +76,7 @@ public void getValue() { @Test - public void getValueWithoutResultsReturnsNull() { + public void getValueWithNullResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(null) @@ -80,6 +89,20 @@ public void getValueWithoutResultsReturnsNull() { assertThat(value).isEqualTo(null); } + @Test + public void getValueWithoutResultsReturnsNull() { + // Arrange + Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() + .item(new HashMap<>()) + .build()); + + // Act + String value = provider.getValue("key"); + + // Assert + assertThat(value).isEqualTo(null); + } + @Test public void getValueWithMalformedRowThrows() { // Arrange @@ -92,7 +115,7 @@ public void getValueWithMalformedRowThrows() { .build()); // Act Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - String value = provider.getValue(key); + provider.getValue(key); }); } @@ -144,6 +167,25 @@ public void getValuesWithoutResultsReturnsNull() { assertThat(values.size()).isEqualTo(0); } + @Test + public void getMultipleValuesMissingSortKey_throwsException() { + // Arrange + String key = "Key1"; + HashMap<String, AttributeValue> item = new HashMap<String, AttributeValue>(); + item.put("id", AttributeValue.fromS(key)); + item.put("value", AttributeValue.fromS("somevalue")); + QueryResponse response = QueryResponse.builder() + .items(item) + .build(); + Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); + + // Assert + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { + // Act + provider.getMultipleValues(key); + }); + } + @Test public void getValuesWithMalformedRowThrows() { // Arrange @@ -160,9 +202,25 @@ public void getValuesWithMalformedRowThrows() { // Assert Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { // Act - Map<String, String> values = provider.getMultipleValues(key); + provider.getMultipleValues(key); }); } + @Test + public void testDynamoDBBuilderMissingCacheManager_throwsException() { + + // Act & Assert + assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() + .withTable("table") + .build()); + } + @Test + public void testDynamoDBBuilderMissingTable_throwsException() { + + // Act & Assert + assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() + .withCacheManager(new CacheManager()) + .build()); + } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java index fca0a9362..e1cb72be9 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java @@ -25,7 +25,11 @@ import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; import software.amazon.awssdk.services.ssm.SsmClient; -import software.amazon.awssdk.services.ssm.model.*; +import software.amazon.awssdk.services.ssm.model.GetParameterRequest; +import software.amazon.awssdk.services.ssm.model.GetParameterResponse; +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 java.util.ArrayList; import java.util.List; @@ -35,7 +39,9 @@ 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.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; public class ParamManagerIntegrationTest { 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 new file mode 100644 index 000000000..ee61691c1 --- /dev/null +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.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; + +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; + +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +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() { + + // Act + SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); + + // Assert + assertNotNull(secretsProvider); + } + + @Test + public void testGetSSMProvider() { + + // Act + SSMProvider ssmProvider = ParamManager.getSsmProvider(); + + // Assert + assertNotNull(ssmProvider); + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java index da299c38d..e55f3d7e6 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java @@ -20,16 +20,26 @@ import org.mockito.Captor; import org.mockito.Mock; import software.amazon.awssdk.services.ssm.SsmClient; -import software.amazon.awssdk.services.ssm.model.*; +import software.amazon.awssdk.services.ssm.model.GetParameterRequest; +import software.amazon.awssdk.services.ssm.model.GetParameterResponse; +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.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Map; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +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; public class SSMProviderTest { @@ -37,6 +47,9 @@ public class SSMProviderTest { @Mock SsmClient client; + @Mock + TransformationManager transformationManager; + @Captor ArgumentCaptor<GetParameterRequest> paramCaptor; @@ -181,10 +194,24 @@ public void getMultipleWithNextToken() { 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"); + } + 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); + provider.withMaxAge(2, ChronoUnit.DAYS); + provider.recursive(); } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java index 611f05fa9..2ab72ffdd 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java @@ -24,10 +24,14 @@ import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +import java.time.temporal.ChronoUnit; import java.util.Base64; 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; public class SecretsProviderTest { @@ -35,6 +39,9 @@ public class SecretsProviderTest { @Mock SecretsManagerClient client; + @Mock + TransformationManager transformationManager; + @Captor ArgumentCaptor<GetSecretValueRequest> paramCaptor; @@ -55,6 +62,8 @@ public void getValue() { 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); @@ -75,4 +84,24 @@ public void getValueBase64() { assertThat(value).isEqualTo(expectedValue); assertThat(paramCaptor.getValue().secretId()).isEqualTo(key); } + + @Test + public void getMultipleValuesThrowsException() { + + // Act & Assert + assertThatRuntimeException().isThrownBy(() -> provider.getMultipleValues("path")) + .withMessage("Impossible to get multiple values from AWS Secrets Manager"); + + } + + @Test + public void testSecretsProviderBuilderMissingCacheManager_throwsException() { + + // Act & Assert + assertThatIllegalStateException().isThrownBy(() -> SecretsProvider.builder() + .withClient(client) + .withTransformationManager(transformationManager) + .build()) + .withMessage("No CacheManager provided, please provide one"); + } } 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 index 2edbc8b24..c390a051e 100644 --- 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 @@ -14,7 +14,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.*; +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; public class LambdaParametersAspectTest { 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 index 0fcfa8c51..6b6548071 100644 --- 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 @@ -15,10 +15,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; import java.util.Base64; 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; @@ -27,6 +29,8 @@ public class TransformationManagerTest { TransformationManager manager; + Class<BasicTransformer> basicTransformer = BasicTransformer.class; + @BeforeEach public void setup() { manager = new TransformationManager(); @@ -58,6 +62,14 @@ public void performBasicTransformation_notBasicTransformer_shouldThrowException( .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); @@ -82,4 +94,12 @@ public void performComplexTransformation_shouldPerformTransformation() { 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/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java index 9742299ee..f1b248fae 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java @@ -13,7 +13,21 @@ */ package software.amazon.lambda.powertools.utilities; -import com.amazonaws.services.lambda.runtime.events.*; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent; +import com.amazonaws.services.lambda.runtime.events.KafkaEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +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.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectReader; import org.slf4j.Logger; 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 d8af1c0cb..549418263 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 @@ -45,6 +45,7 @@ public static JsonConfig get() { new JsonFunction() ); private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() + .withSilentTypeErrors(true) .withFunctionRegistry(customFunctions) .build(); private JmesPath<JsonNode> jmesPath = new JacksonRuntime(configuration, getObjectMapper()); diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java index 6b097af62..8628fd1d2 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java @@ -46,25 +46,29 @@ protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> argum String decompressString = decompress(decode(encodedString.getBytes(UTF_8))); + if (decompressString == null) { + return runtime.createNull(); + } + return runtime.createString(decompressString); } public static String decompress(byte[] compressed) { - if ((compressed == null) || (compressed.length == 0)) { - return ""; + if (compressed == null || compressed.length == 0) { + return null; + } + if (!isCompressed(compressed)) { + return new String(compressed, UTF_8); } try { StringBuilder out = new StringBuilder(); - if (isCompressed(compressed)) { - GZIPInputStream gzipStream = new GZIPInputStream(new ByteArrayInputStream(compressed)); - BufferedReader bf = new BufferedReader(new InputStreamReader(gzipStream, UTF_8)); - String line; - while ((line = bf.readLine()) != null) { - out.append(line); - } - } else { - out.append(Arrays.toString(compressed)); + GZIPInputStream gzipStream = new GZIPInputStream(new ByteArrayInputStream(compressed)); + BufferedReader bf = new BufferedReader(new InputStreamReader(gzipStream, UTF_8)); + + String line; + while ((line = bf.readLine()) != null) { + out.append(line); } return out.toString(); } catch (IOException e) { 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 90143b2a0..5055d7086 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 @@ -13,11 +13,26 @@ */ package software.amazon.lambda.powertools.utilities; -import com.amazonaws.services.lambda.runtime.events.*; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent; +import com.amazonaws.services.lambda.runtime.events.KafkaEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +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.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import software.amazon.lambda.powertools.utilities.model.Basket; +import software.amazon.lambda.powertools.utilities.model.Order; import software.amazon.lambda.powertools.utilities.model.Product; import java.util.HashMap; @@ -129,7 +144,23 @@ public void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent public void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGatewayProxyRequestEvent event) { assertThatThrownBy(() -> extractDataFrom(event).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) - .hasMessageContaining("consider using 'as' instead"); + .hasMessageContaining("consider using 'as' instead") + .hasMessageContaining("Cannot load the event as a list of"); + } + + @ParameterizedTest + @Event(value = "custom_event_map.json", type = HashMap.class) + public 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() { + assertThatThrownBy(() -> extractDataFrom(null).asListOf(Product.class)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Event content is null: the event may be malformed (missing fields)"); } @ParameterizedTest @@ -145,7 +176,14 @@ public void testDeserializeSQSEventBodyAsWrongObjectType_shouldThrowException(SQ public void testDeserializeAPIGatewayNoBody_shouldThrowException(APIGatewayProxyRequestEvent event) { assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Event content is null"); + .hasMessage("Event content is null: the event may be malformed (missing fields)"); + } + + @Test + public 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"); } @ParameterizedTest @@ -164,9 +202,84 @@ public void testDeserializeProductAsProduct_shouldReturnProduct() { private void assertProduct(Product product) { -assertThat(product) + assertThat(product) .isEqualTo(new Product(1234, "product", 42)) .usingRecursiveComparison(); } + @ParameterizedTest + @Event(value = "scheduled_event.json", type = ScheduledEvent.class) + public 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) { + Product product = extractDataFrom(event).as(Product.class); + assertProduct(product); + } + + @ParameterizedTest + @Event(value = "cwl_event.json", type = CloudWatchLogsEvent.class) + public 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) { + List<Product> products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(1); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "amq_event.json", type = ActiveMQEvent.class) + public void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent event) { + List<Product> products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(1); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "rabbitmq_event.json", type = RabbitMQEvent.class) + public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEvent event) { + List<Product> products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(1); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) + public void testDeserializeKasipEventMessageAsListShouldReturnList(KinesisAnalyticsStreamsInputPreprocessingEvent event) { + List<Product> products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(1); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) + public void testDeserializeKafipEventMessageAsListShouldReturnList(KinesisAnalyticsFirehoseInputPreprocessingEvent event) { + List<Product> products = extractDataFrom(event).asListOf(Product.class); + assertThat(products).hasSize(1); + assertProduct(products.get(0)); + } + + @ParameterizedTest + @Event(value = "apigwv2_event.json", type = APIGatewayV2HTTPEvent.class) + public 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) { + Product product = extractDataFrom(event).as(Product.class); + assertProduct(product); + } + } 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 8c617a634..8e574eba6 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,6 +16,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import io.burt.jmespath.Expression; +import io.burt.jmespath.JmesPathType; import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; @@ -25,6 +26,16 @@ public class Base64GZipFunctionTest { + @Test + public void testConstructor() { + Base64GZipFunction base64GZipFunction = new Base64GZipFunction(); + assertThat(base64GZipFunction.name()).isEqualTo("powertools_base64_gzip"); + assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo(JmesPathType.STRING.name().toLowerCase()); + assertThat(base64GZipFunction.argumentConstraints().minArity()).isEqualTo(1); + assertThat(base64GZipFunction.argumentConstraints().maxArity()).isEqualTo(1); + + } + @Test public void testPowertoolsGzip() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); @@ -33,4 +44,38 @@ public void testPowertoolsGzip() throws IOException { assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{ \"id\": 43242, \"name\": \"FooBar XY\", \"price\": 258}"); } + + @Test + public 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('')"); + JsonNode result = expression.search(event); + assertThat(result.getNodeType()).isEqualTo(JsonNodeType.NULL); + } + + @Test + public 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)"); + JsonNode result = expression.search(event); + + assertThat(result.getNodeType()).isEqualTo(JsonNodeType.NULL); + } + + @Test + public void testBase64GzipDecompressNull() { + String result = Base64GZipFunction.decompress(null); + assertThat(result).isNull(); + } + + @Test + public 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)"); + 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/model/Order.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java new file mode 100644 index 000000000..eca36c222 --- /dev/null +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.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.utilities.model; + +import java.util.HashMap; +import java.util.Map; + +public class Order { + private Map<String, Product> orders = new HashMap<>(); + + public Order() { + } + + public Map<String, Product> getProducts() { + return orders; + } + + public void setProducts(Map<String, Product> products) { + this.orders = products; + } + +} diff --git a/powertools-serialization/src/test/resources/alb_event.json b/powertools-serialization/src/test/resources/alb_event.json new file mode 100644 index 000000000..d2b0d3cda --- /dev/null +++ b/powertools-serialization/src/test/resources/alb_event.json @@ -0,0 +1,28 @@ +{ + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a" + } + }, + "httpMethod": "GET", + "path": "/lambda", + "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-1.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": "{\"id\":1234, \"name\":\"product\", \"price\":42}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/amq_event.json b/powertools-serialization/src/test/resources/amq_event.json new file mode 100644 index 000000000..00923d5e5 --- /dev/null +++ b/powertools-serialization/src/test/resources/amq_event.json @@ -0,0 +1,29 @@ +{ + "eventSource": "aws:mq", + "eventSourceArn": "arn:aws:mq:us-west-2:111122223333:broker:test:b-9bcfa592-423a-4942-879d-eb284b418fc8", + "messages": [ + { + "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1", + "messageType": "jms/text-message", + "deliveryMode": 1, + "replyTo": null, + "type": null, + "expiration": "60000", + "priority": 1, + "correlationId": "myJMSCoID", + "redelivered": false, + "destination": { + "physicalName": "testQueue" + }, + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==", + "timestamp": 1598827811958, + "brokerInTime": 1598827811958, + "brokerOutTime": 1598827811959, + "properties": { + "index": "1", + "doAlarm": "false", + "myCustomProperty": "value" + } + } + ] +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/apigwv2_event.json b/powertools-serialization/src/test/resources/apigwv2_event.json new file mode 100644 index 000000000..db4fc0f95 --- /dev/null +++ b/powertools-serialization/src/test/resources/apigwv2_event.json @@ -0,0 +1,57 @@ +{ + "version": "V2", + "routeKey": "routeKey", + "rawPath": "rawPath", + "rawQueryString": "rawQueryString", + "cookies": + ["foo", "bar"] + , + "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" + }, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "body": "{\"id\":1234, \"name\":\"product\", \"price\":42}", + "isBase64Encoded": false, + "requestContext": { + "routeKey": "routeKey", + "accountId": "123456789012", + "stage": "prod", + "apiId": "1234567890", + "domainName": "domainName", + "domainPrefix": "domainPrefix", + "time": "09/Apr/2015:12:34:56 +0000", + "timeEpoch": 1428582896000, + "http": { + "method": "POST", + "path": "/path/to/resource", + "protocol": "HTTP/1.1", + "sourceIp": "1.1.1.1", + "userAgent": "Chrome" + } + } +} diff --git a/powertools-serialization/src/test/resources/cfcr_event.json b/powertools-serialization/src/test/resources/cfcr_event.json new file mode 100644 index 000000000..58d054c06 --- /dev/null +++ b/powertools-serialization/src/test/resources/cfcr_event.json @@ -0,0 +1,20 @@ +{ + "RequestType": "requestType", + "ServiceToken": "serviceToken", + "ResponseUrl": "responseUrl", + "StackId": "stackId", + "RequestId": "requestId", + "LogicalResourceId": "logicalResourceId", + "ResourceType": "resourceType", + "ResourceProperties": { + "id": 1234, + "name": "product", + "price": 42 + }, + "OldResourceProperties": { + "id": 1234, + "name": "product", + "price": 40 + } +} + diff --git a/powertools-serialization/src/test/resources/custom_event.json b/powertools-serialization/src/test/resources/custom_event.json index 13103c434..918cad81f 100644 --- a/powertools-serialization/src/test/resources/custom_event.json +++ b/powertools-serialization/src/test/resources/custom_event.json @@ -1,6 +1,6 @@ { "basket": { - "products" : [ + "products": [ { "id": 43242, "name": "FooBar XY", diff --git a/powertools-serialization/src/test/resources/custom_event_gzip.json b/powertools-serialization/src/test/resources/custom_event_gzip.json index d212052d0..2cf088092 100644 --- a/powertools-serialization/src/test/resources/custom_event_gzip.json +++ b/powertools-serialization/src/test/resources/custom_event_gzip.json @@ -1,12 +1,13 @@ { "basket": { - "products" : [ + "products": [ { "id": 43242, "name": "FooBar XY", "price": 258 } ], - "hiddenProduct": "H4sIAAAAAAAA/6vmUlBQykxRslIwMTYyMdIBcfMSc1OBAkpu+flOiUUKEZFKYOGCosxkkLiRqQVXLQDnWo6bOAAAAA==" + "hiddenProduct": "H4sIAAAAAAAA/6vmUlBQykxRslIwMTYyMdIBcfMSc1OBAkpu+flOiUUKEZFKYOGCosxkkLiRqQVXLQDnWo6bOAAAAA==", + "encodedString": "dGVzdA==" } } \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/custom_event_map.json b/powertools-serialization/src/test/resources/custom_event_map.json new file mode 100644 index 000000000..7d3f076d7 --- /dev/null +++ b/powertools-serialization/src/test/resources/custom_event_map.json @@ -0,0 +1,9 @@ +{ + "products": { + "12345": { + "id": 43242, + "name": "FooBar XY", + "price": 258 + } + } +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/cwl_event.json b/powertools-serialization/src/test/resources/cwl_event.json new file mode 100644 index 000000000..911ab1b3a --- /dev/null +++ b/powertools-serialization/src/test/resources/cwl_event.json @@ -0,0 +1,5 @@ +{ + "awslogs": { + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==" + } +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/kafip_event.json b/powertools-serialization/src/test/resources/kafip_event.json new file mode 100644 index 000000000..01196256c --- /dev/null +++ b/powertools-serialization/src/test/resources/kafip_event.json @@ -0,0 +1,14 @@ +{ + "invocationId": "arn:aws:iam::EXAMPLE", + "applicationArn": "arn:aws:kinesis:EXAMPLE", + "streamArn": "arn:aws:kinesis:EXAMPLE", + "records": [ + { + "kinesisFirehoseRecordMetadata": { + "approximateArrivalTimestamp": 1428537600 + }, + "recordId": "record-id", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=" + } + ] +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/kasip_event.json b/powertools-serialization/src/test/resources/kasip_event.json new file mode 100644 index 000000000..78bc9a3fb --- /dev/null +++ b/powertools-serialization/src/test/resources/kasip_event.json @@ -0,0 +1,17 @@ +{ + "invocationId": "arn:aws:iam::EXAMPLE", + "applicationArn": "arn:aws:kinesis:EXAMPLE", + "streamArn": "arn:aws:kinesis:EXAMPLE", + "records": [ + { + "kinesisStreamRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "partitionKey": "partitionKey-03", + "shardId": "12", + "approximateArrivalTimestamp": 1428537600 + }, + "recordId": "record-id", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=" + } + ] +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/kf_event.json b/powertools-serialization/src/test/resources/kf_event.json new file mode 100644 index 000000000..e36bc4c3f --- /dev/null +++ b/powertools-serialization/src/test/resources/kf_event.json @@ -0,0 +1,12 @@ +{ + "invocationId": "invocationIdExample", + "deliveryStreamArn": "arn:aws:kinesis:EXAMPLE", + "region": "us-east-1", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "approximateArrivalTimestamp": 1495072949453, + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==" + } + ] +} \ No newline at end of file diff --git a/powertools-serialization/src/test/resources/rabbitmq_event.json b/powertools-serialization/src/test/resources/rabbitmq_event.json new file mode 100644 index 000000000..698e37143 --- /dev/null +++ b/powertools-serialization/src/test/resources/rabbitmq_event.json @@ -0,0 +1,51 @@ +{ + "eventSource": "aws:rmq", + "eventSourceArn": "arn:aws:mq:us-west-2:111122223333:broker:pizzaBroker:b-9bcfa592-423a-4942-879d-eb284b418fc8", + "rmqMessagesByQueue": { + "pizzaQueue::/": [ + { + "basicProperties": { + "contentType": "text/plain", + "contentEncoding": null, + "headers": { + "header1": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 49 + ] + }, + "header2": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 50 + ] + }, + "numberInHeader": 10 + }, + "deliveryMode": 1, + "priority": 34, + "correlationId": null, + "replyTo": null, + "expiration": "60000", + "messageId": null, + "timestamp": "Jan 1, 1970, 12:33:41 AM", + "type": null, + "userId": "AIDACKCEVSQ6C2EXAMPLE", + "appId": null, + "clusterId": null, + "bodySize": 80 + }, + "redelivered": false, + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==" + } + ] + } +} diff --git a/powertools-serialization/src/test/resources/scheduled_event.json b/powertools-serialization/src/test/resources/scheduled_event.json new file mode 100644 index 000000000..9a65f4bd4 --- /dev/null +++ b/powertools-serialization/src/test/resources/scheduled_event.json @@ -0,0 +1,12 @@ +{ + "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", + "detail-type": "Scheduled Event", + "source": "aws.events", + "account": "123456789012", + "time": "1970-01-01T00:00:00Z", + "region": "eu-central-1", + "resources": [ + "arn:aws:events:eu-central-1:123456789012:rule/my-schedule" + ], + "detail": {"id":1234, "name":"product", "price":42} +} \ No newline at end of file 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 index 0d1c9c35a..43a089d2c 100644 --- 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 @@ -23,7 +23,6 @@ import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 76c434614..1469183ef 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -1,6 +1,6 @@ <?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" +<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> @@ -112,6 +112,11 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file 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 3fd964226..b29adf8d7 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 @@ -31,19 +31,19 @@ * is just a wrapper of {@link JsonConfig}. */ public class ValidationConfig { - private ValidationConfig() { - } + private SpecVersion.VersionFlag jsonSchemaVersion = SpecVersion.VersionFlag.V7; + private JsonSchemaFactory factory = JsonSchemaFactory.getInstance(jsonSchemaVersion); - private static class ConfigHolder { - private final static ValidationConfig instance = new ValidationConfig(); + private ValidationConfig() { } public static ValidationConfig get() { return ConfigHolder.instance; } - private SpecVersion.VersionFlag jsonSchemaVersion = SpecVersion.VersionFlag.V7; - private JsonSchemaFactory factory = JsonSchemaFactory.getInstance(jsonSchemaVersion); + public SpecVersion.VersionFlag getSchemaVersion() { + return jsonSchemaVersion; + } /** * Set the version of the json schema specifications (default is V7) @@ -57,16 +57,12 @@ public void setSchemaVersion(SpecVersion.VersionFlag version) { } } - public SpecVersion.VersionFlag getSchemaVersion() { - return jsonSchemaVersion; - } - /** * Add a custom {@link io.burt.jmespath.function.Function} to JMESPath * {@link Base64Function} and {@link Base64GZipFunction} are already built-in. * * @param function the function to add - * @param <T> Must extends {@link BaseFunction} + * @param <T> Must extend {@link BaseFunction} */ public <T extends BaseFunction> void addFunction(T function) { JsonConfig.get().addFunction(function); @@ -98,4 +94,8 @@ public JmesPath<JsonNode> getJmesPath() { public ObjectMapper getObjectMapper() { return JsonConfig.get().getObjectMapper(); } + + private static class ConfigHolder { + private final static 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 9b73806a5..3c2322edc 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 @@ -13,15 +13,6 @@ */ package software.amazon.lambda.powertools.validation; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -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 com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; import com.fasterxml.jackson.core.JsonProcessingException; @@ -33,6 +24,14 @@ import io.burt.jmespath.Expression; import software.amazon.lambda.powertools.validation.internal.ValidationAspect; +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; + /** * Validation utility, used to manually validate Json against Json Schema */ @@ -79,7 +78,7 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope) throw new ValidationException("Envelope not found in the object"); } } catch (Exception e) { - throw new ValidationException("Cannot find envelope <"+envelope+"> in the object <"+obj+">", e); + throw new ValidationException("Cannot find envelope <" + envelope + "> in the object <" + obj + ">", e); } if (subNode.getNodeType() == JsonNodeType.ARRAY) { subNode.forEach(jsonNode -> validate(jsonNode, jsonSchema)); @@ -120,7 +119,7 @@ public static void validate(Object obj, JsonSchema jsonSchema) throws Validation try { jsonNode = ValidationConfig.get().getObjectMapper().valueToTree(obj); } catch (Exception e) { - throw new ValidationException("Object <"+obj+"> is not valid against the schema provided", e); + throw new ValidationException("Object <" + obj + "> is not valid against the schema provided", e); } validate(jsonNode, jsonSchema); @@ -149,7 +148,7 @@ public static void validate(String json, JsonSchema jsonSchema) throws Validatio try { jsonNode = ValidationConfig.get().getObjectMapper().readTree(json); } catch (Exception e) { - throw new ValidationException("Json <"+json+"> is not valid against the schema provided", e); + throw new ValidationException("Json <" + json + "> is not valid against the schema provided", e); } validate(jsonNode, jsonSchema); @@ -178,7 +177,7 @@ public static void validate(Map<String, Object> map, JsonSchema jsonSchema) thro try { jsonNode = ValidationConfig.get().getObjectMapper().valueToTree(map); } catch (Exception e) { - throw new ValidationException("Map <"+map+"> cannot be converted to json for validation", e); + throw new ValidationException("Map <" + map + "> cannot be converted to json for validation", e); } validate(jsonNode, jsonSchema); @@ -253,12 +252,9 @@ private static JsonSchema createJsonSchema(String schema) { if (schema.startsWith(CLASSPATH)) { String filePath = schema.substring(CLASSPATH.length()); try (InputStream schemaStream = ValidationAspect.class.getResourceAsStream(filePath)) { - if (schemaStream == null) { - throw new IllegalArgumentException("'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); - } jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream); - } catch (IOException e) { + } catch (Exception e) { throw new IllegalArgumentException("'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); } } else { 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 be19a50a0..e659abbeb 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 @@ -13,7 +13,26 @@ */ package software.amazon.lambda.powertools.validation.internal; -import com.amazonaws.services.lambda.runtime.events.*; +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.APIGatewayV2WebSocketResponse; +import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent; +import com.amazonaws.services.lambda.runtime.events.KafkaEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +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.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.networknt.schema.JsonSchema; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; 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 d5e9332ed..6669e46b1 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 @@ -13,13 +13,17 @@ import java.util.HashMap; import java.util.Map; -import static org.assertj.core.api.Assertions.*; +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.assertj.core.api.Assertions.assertThatThrownBy; import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; public class ValidationUtilsTest { - private JsonSchema schema = getJsonSchema("classpath:/schema_v7.json"); + private String schemaString = "classpath:/schema_v7.json"; + private JsonSchema schema = getJsonSchema(schemaString); @BeforeEach public void setup() { @@ -44,8 +48,11 @@ public void testLoadSchemaV7KO() { @Test public void testLoadMetaSchema_NoValidation() { - ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V201909); - getJsonSchema("classpath:/schemas/meta_schema_V201909", false); + ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); + + assertThatNoException().isThrownBy(() -> { + getJsonSchema("classpath:/schema_v7_ko.json", false); + }); } @Test @@ -94,7 +101,9 @@ public void testLoadSchemaNotFound() { public void testValidateJsonNodeOK() throws IOException { JsonNode node = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ok.json")); - validate(node, schema); + assertThatNoException().isThrownBy(() -> { + validate(node, schemaString); + }); } @Test @@ -106,12 +115,15 @@ public void testValidateJsonNodeKO() throws IOException { @Test public void testValidateMapOK() { + Map<String, Object> map = new HashMap<>(); map.put("id", 43242); map.put("name", "FooBar XY"); map.put("price", 258); - validate(map, schema); + assertThatNoException().isThrownBy(() -> { + validate(map, schemaString); + }); } @Test @@ -123,11 +135,21 @@ public void testValidateMapKO() { assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(map, schema)); } + @Test + public void testValidateMapNotValidJsonObject() { + Map<String, Object> map = new HashMap<>(); + map.put("1234", new Object()); + + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(map, schema)); + } + @Test public void testValidateStringOK() { String json = "{\n \"id\": 43242,\n \"name\": \"FooBar XY\",\n \"price\": 258\n}"; - validate(json, schema); + assertThatNoException().isThrownBy(() -> { + validate(json, schemaString); + }); } @Test @@ -140,14 +162,22 @@ public void testValidateStringKO() { @Test public void testValidateObjectOK() { Product product = new Product(42, "FooBar", 42); - validate(product, schema); + + assertThatNoException().isThrownBy(() -> { + validate(product, schemaString); + }); } @Test public void testValidateObjectKO() { - Product product = new Product(42, "FooBar", -12); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(product, schema)); + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(new Object(), schema)); + } + + @Test + public void testValidateObjectNotValidJson() { + + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(new Object(), schema)); } @Test @@ -158,7 +188,10 @@ public void testValidateSubObjectOK() { basket.add(product); basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - validate(event, schema, "basket.products[0]"); + + assertThatNoException().isThrownBy(() -> { + validate(event, schemaString, "basket.products[0]"); + }); } @Test @@ -182,7 +215,7 @@ public void testValidateSubObjectListOK() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - validate(event, schema, "basket.products[*]"); + assertThatNoException().isThrownBy(() -> validate(event, schema, "basket.products[*]")); } @Test @@ -203,7 +236,7 @@ public void testValidateSubObjectNotFound() { Basket basket = new Basket(); basket.add(product); MyCustomEvent event = new MyCustomEvent(basket); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(event, schema, "basket.product")); + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(event, schema, "basket.")); } @Test @@ -226,7 +259,7 @@ public void testValidateSubObjectJsonString() { basket.setHiddenProduct("ewogICJpZCI6IDQzMjQyLAogICJuYW1lIjogIkZvb0JhciBYWSIsCiAgInByaWNlIjogMjU4Cn0="); MyCustomEvent event = new MyCustomEvent(basket); - validate(event, schema, "basket.powertools_base64(hiddenProduct)"); + assertThatNoException().isThrownBy(() -> validate(event, schema, "basket.powertools_base64(hiddenProduct)")); } @Test @@ -243,7 +276,13 @@ public void testValidateSubObjectSimpleString() { @Test public void testValidateSubObjectWithoutEnvelope() { Product product = new Product(42, "BarBazFoo", 42); - validate(product, schema, null); + assertThatNoException().isThrownBy(() -> validate(product, schema, null)); + } + + @Test + public void testValidateSubObjectWithEmptyEnvelope() { + Product product = new Product(42, "BarBazFoo", 42); + assertThatNoException().isThrownBy(() -> validate(product, schema, "")); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java similarity index 82% rename from powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandler.java rename to powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java index cd6719b0f..1c64e7da1 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java @@ -15,14 +15,13 @@ 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.validation.Validation; -public class SQSHandler implements RequestHandler<SQSEvent, String> { +public class GenericSchemaV7Handler<T> implements RequestHandler<T, String> { - @Override @Validation(inboundSchema = "classpath:/schema_v7.json") - public String handleRequest(SQSEvent input, Context context) { + @Override + public String handleRequest(T input, Context context) { return "OK"; } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandler.java deleted file mode 100644 index 7132fcb9b..000000000 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES 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 software.amazon.lambda.powertools.validation.Validation; - -public class KinesisHandler implements RequestHandler<KinesisEvent, String> { - - @Validation(inboundSchema = "classpath:/schema_v7.json") - @Override - public String handleRequest(KinesisEvent input, Context context) { - return "OK"; - } -} diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundClasspathHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundClasspathHandler.java deleted file mode 100644 index 59b6cc7b5..000000000 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundClasspathHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES 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.APIGatewayProxyRequestEvent; -import software.amazon.lambda.powertools.validation.Validation; - - -public class ValidationInboundClasspathHandler implements RequestHandler<APIGatewayProxyRequestEvent, String> { - - @Override - @Validation(inboundSchema = "classpath:/schema_v7.json") - public String handleRequest(APIGatewayProxyRequestEvent input, Context context) { - return "OK"; - } -} 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/ValidationInboundStringHandler.java index e27f31129..2e62ba88d 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/ValidationInboundStringHandler.java @@ -76,7 +76,7 @@ public class ValidationInboundStringHandler implements RequestHandler<APIGateway " },\n" + " \"additionalProperties\": true\n" + "}"; - + @Override @Validation(inboundSchema = schema) public String handleRequest(APIGatewayV2HTTPEvent input, Context context) { 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 new file mode 100644 index 000000000..9df0ff508 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.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.validation.internal; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2WebSocketResponse; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public class ResponseEventsArgumentsProvider implements ArgumentsProvider { + + @Override + 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); + + ApplicationLoadBalancerResponseEvent albResponseEvent = new ApplicationLoadBalancerResponseEvent(); + albResponseEvent.setBody(body); + + KinesisAnalyticsInputPreprocessingResponse kaipResponse = new KinesisAnalyticsInputPreprocessingResponse(); + List records = new ArrayList<KinesisAnalyticsInputPreprocessingResponse.Record>(); + ByteBuffer buffer = ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8)); + records.add(new KinesisAnalyticsInputPreprocessingResponse.Record("1", KinesisAnalyticsInputPreprocessingResponse.Result.Ok, buffer)); + kaipResponse.setRecords(records); + + return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse, 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 2c66885d6..63e93c3ac 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 @@ -14,39 +14,129 @@ package software.amazon.lambda.powertools.validation.internal; 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.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; +import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent; +import com.amazonaws.services.lambda.runtime.events.KafkaEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsFirehoseInputPreprocessingEvent; +import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsStreamsInputPreprocessingEvent; import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +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.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.networknt.schema.SpecVersion; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; 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.ArgumentsSource; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +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.*; +import software.amazon.lambda.powertools.validation.handlers.GenericSchemaV7Handler; +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.model.MyCustomEvent; import java.io.IOException; +import java.util.stream.Stream; 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.mockito.Mockito.when; + public class ValidationAspectTest { + @Mock + Validation validation; + @Mock + Signature signature; @Mock private Context context; + @Mock + private ProceedingJoinPoint pjp; + private ValidationAspect validationAspect = new ValidationAspect(); + + private static Stream<Arguments> provideEventAndEventType() { + return Stream.of( + Arguments.of("/sns_event.json", SNSEvent.class), + Arguments.of("/scheduled_event.json", ScheduledEvent.class), + Arguments.of("/alb_event.json", ApplicationLoadBalancerRequestEvent.class), + Arguments.of("/cwl_event.json", CloudWatchLogsEvent.class), + Arguments.of("/cfcr_event.json", CloudFormationCustomResourceEvent.class), + Arguments.of("/kf_event.json", KinesisFirehoseEvent.class), + Arguments.of("/kafka_event.json", KafkaEvent.class), + Arguments.of("/amq_event.json", ActiveMQEvent.class), + Arguments.of("/rabbitmq_event.json", RabbitMQEvent.class), + Arguments.of("/kafip_event.json", KinesisAnalyticsFirehoseInputPreprocessingEvent.class), + Arguments.of("/kasip_event.json", KinesisAnalyticsStreamsInputPreprocessingEvent.class), + Arguments.of("/custom_event.json", MyCustomEvent.class) + + ); + } @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } + @ParameterizedTest + @ArgumentsSource(ResponseEventsArgumentsProvider.class) + public void testValidateOutboundJsonSchema(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"); + + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> { + validationAspect.around(pjp, validation); + }); + } + + @Test + public void testValidateOutboundJsonSchema_APIGWV2() 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); + APIGatewayV2HTTPResponse apiGatewayV2HTTPResponse = new APIGatewayV2HTTPResponse(); + apiGatewayV2HTTPResponse.setBody("{" + + " \"id\": 1," + + " \"name\": \"Lampshade\"," + + " \"price\": 42" + + "}"); + when(pjp.proceed(args)).thenReturn(apiGatewayV2HTTPResponse); + when(validation.inboundSchema()).thenReturn(""); + when(validation.outboundSchema()).thenReturn("classpath:/schema_v7.json"); + + assertThatNoException().isThrownBy(() -> validationAspect.around(pjp, validation)); + } + @Test public void validate_inputOK_schemaInClasspath_shouldValidate() { - ValidationInboundClasspathHandler handler = new ValidationInboundClasspathHandler(); + GenericSchemaV7Handler<APIGatewayProxyRequestEvent> handler = new GenericSchemaV7Handler(); APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody("{" + " \"id\": 1," + @@ -58,7 +148,7 @@ public void validate_inputOK_schemaInClasspath_shouldValidate() { @Test public void validate_inputKO_schemaInClasspath_shouldThrowValidationException() { - ValidationInboundClasspathHandler handler = new ValidationInboundClasspathHandler(); + GenericSchemaV7Handler<APIGatewayProxyRequestEvent> handler = new GenericSchemaV7Handler(); APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody("{" + " \"id\": 1," + @@ -97,7 +187,7 @@ public void validate_SQS() { PojoSerializer<SQSEvent> pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); - SQSHandler handler = new SQSHandler(); + GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } @@ -124,15 +214,16 @@ public void validate_Kinesis() { PojoSerializer<KinesisEvent> pojoSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); KinesisEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/kinesis.json")); - KinesisHandler handler = new KinesisHandler(); + GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } - @Test - public void validate_CustomObject() throws IOException { - MyCustomEvent event = ValidationConfig.get().getObjectMapper().readValue(this.getClass().getResourceAsStream("/custom_event.json"), MyCustomEvent.class); + @ParameterizedTest + @MethodSource("provideEventAndEventType") + public void validateEEvent(String jsonResource, Class eventClass) throws IOException { + Object event = ValidationConfig.get().getObjectMapper().readValue(this.getClass().getResourceAsStream(jsonResource), eventClass); - MyCustomEventHandler handler = new MyCustomEventHandler(); + GenericSchemaV7Handler<Object> handler = new GenericSchemaV7Handler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java index 548ef4660..398f67265 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java @@ -21,6 +21,9 @@ public class Basket { private String hiddenProduct; + public Basket() { + } + public List<Product> getProducts() { return products; } @@ -29,9 +32,6 @@ public void setProducts(List<Product> products) { this.products = products; } - public Basket() { - } - public void add(Product product) { products.add(product); } diff --git a/powertools-validation/src/test/resources/alb_event.json b/powertools-validation/src/test/resources/alb_event.json new file mode 100644 index 000000000..d2b0d3cda --- /dev/null +++ b/powertools-validation/src/test/resources/alb_event.json @@ -0,0 +1,28 @@ +{ + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a" + } + }, + "httpMethod": "GET", + "path": "/lambda", + "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-1.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": "{\"id\":1234, \"name\":\"product\", \"price\":42}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/amq_event.json b/powertools-validation/src/test/resources/amq_event.json new file mode 100644 index 000000000..2f29bdc9f --- /dev/null +++ b/powertools-validation/src/test/resources/amq_event.json @@ -0,0 +1,28 @@ +{ + "eventSource": "aws:mq", + "eventSourceArn": "arn:aws:mq:us-west-2:111122223333:broker:test:b-9bcfa592-423a-4942-879d-eb284b418fc8", + "messages": [ + { + "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1", + "messageType": "jms/text-message", + "deliveryMode": 1, + "replyTo": null, + "type": null, + "expiration": "60000", + "priority": 1, + "redelivered": false, + "destination": { + "physicalName": "testQueue" + }, + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==", + "timestamp": 1598827811958, + "brokerInTime": 1598827811958, + "brokerOutTime": 1598827811959, + "properties": { + "index": "1", + "doAlarm": "false", + "myCustomProperty": "value" + } + } + ] +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/cfcr_event.json b/powertools-validation/src/test/resources/cfcr_event.json new file mode 100644 index 000000000..98b4a9eba --- /dev/null +++ b/powertools-validation/src/test/resources/cfcr_event.json @@ -0,0 +1,20 @@ +{ + "requestType": "requestType", + "serviceToken": "serviceToken", + "responseUrl": "responseUrl", + "stackId": "stackId", + "requestId": "requestId", + "logicalResourceId": "logicalResourceId", + "resourceType": "resourceType", + "resourceProperties": { + "id": 1234, + "name": "product", + "price": 42 + }, + "oldResourceProperties": { + "id": 1234, + "name": "product", + "price": 40 + } +} + diff --git a/powertools-validation/src/test/resources/custom_event.json b/powertools-validation/src/test/resources/custom_event.json index 13103c434..918cad81f 100644 --- a/powertools-validation/src/test/resources/custom_event.json +++ b/powertools-validation/src/test/resources/custom_event.json @@ -1,6 +1,6 @@ { "basket": { - "products" : [ + "products": [ { "id": 43242, "name": "FooBar XY", diff --git a/powertools-validation/src/test/resources/custom_event_gzip.json b/powertools-validation/src/test/resources/custom_event_gzip.json index d212052d0..165db7071 100644 --- a/powertools-validation/src/test/resources/custom_event_gzip.json +++ b/powertools-validation/src/test/resources/custom_event_gzip.json @@ -1,6 +1,6 @@ { "basket": { - "products" : [ + "products": [ { "id": 43242, "name": "FooBar XY", diff --git a/powertools-validation/src/test/resources/cwl_event.json b/powertools-validation/src/test/resources/cwl_event.json new file mode 100644 index 000000000..5dd0f6657 --- /dev/null +++ b/powertools-validation/src/test/resources/cwl_event.json @@ -0,0 +1,5 @@ +{ + "awsLogs": { + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==" + } +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/kafip_event.json b/powertools-validation/src/test/resources/kafip_event.json new file mode 100644 index 000000000..01196256c --- /dev/null +++ b/powertools-validation/src/test/resources/kafip_event.json @@ -0,0 +1,14 @@ +{ + "invocationId": "arn:aws:iam::EXAMPLE", + "applicationArn": "arn:aws:kinesis:EXAMPLE", + "streamArn": "arn:aws:kinesis:EXAMPLE", + "records": [ + { + "kinesisFirehoseRecordMetadata": { + "approximateArrivalTimestamp": 1428537600 + }, + "recordId": "record-id", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=" + } + ] +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/kafka_event.json b/powertools-validation/src/test/resources/kafka_event.json new file mode 100644 index 000000000..cf1bad615 --- /dev/null +++ b/powertools-validation/src/test/resources/kafka_event.json @@ -0,0 +1,27 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-3432434/4834-3547-3455-9872-7929", + "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-01": [ + { + "topic": "mytopic1", + "partition": 0, + "offset": 15, + "timestamp": 1596480920837, + "timestampType": "CREATE_TIME", + "value": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=" + } + ], + "mytopic-02": [ + { + "topic": "mytopic2", + "partition": 0, + "offset": 15, + "timestamp": 1596480920838, + "timestampType": "CREATE_TIME", + "value": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==" + } + ] + } +} diff --git a/powertools-validation/src/test/resources/kasip_event.json b/powertools-validation/src/test/resources/kasip_event.json new file mode 100644 index 000000000..78bc9a3fb --- /dev/null +++ b/powertools-validation/src/test/resources/kasip_event.json @@ -0,0 +1,17 @@ +{ + "invocationId": "arn:aws:iam::EXAMPLE", + "applicationArn": "arn:aws:kinesis:EXAMPLE", + "streamArn": "arn:aws:kinesis:EXAMPLE", + "records": [ + { + "kinesisStreamRecordMetadata": { + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "partitionKey": "partitionKey-03", + "shardId": "12", + "approximateArrivalTimestamp": 1428537600 + }, + "recordId": "record-id", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=" + } + ] +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/kf_event.json b/powertools-validation/src/test/resources/kf_event.json new file mode 100644 index 000000000..e36bc4c3f --- /dev/null +++ b/powertools-validation/src/test/resources/kf_event.json @@ -0,0 +1,12 @@ +{ + "invocationId": "invocationIdExample", + "deliveryStreamArn": "arn:aws:kinesis:EXAMPLE", + "region": "us-east-1", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "approximateArrivalTimestamp": 1495072949453, + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==" + } + ] +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/rabbitmq_event.json b/powertools-validation/src/test/resources/rabbitmq_event.json new file mode 100644 index 000000000..698e37143 --- /dev/null +++ b/powertools-validation/src/test/resources/rabbitmq_event.json @@ -0,0 +1,51 @@ +{ + "eventSource": "aws:rmq", + "eventSourceArn": "arn:aws:mq:us-west-2:111122223333:broker:pizzaBroker:b-9bcfa592-423a-4942-879d-eb284b418fc8", + "rmqMessagesByQueue": { + "pizzaQueue::/": [ + { + "basicProperties": { + "contentType": "text/plain", + "contentEncoding": null, + "headers": { + "header1": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 49 + ] + }, + "header2": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 50 + ] + }, + "numberInHeader": 10 + }, + "deliveryMode": 1, + "priority": 34, + "correlationId": null, + "replyTo": null, + "expiration": "60000", + "messageId": null, + "timestamp": "Jan 1, 1970, 12:33:41 AM", + "type": null, + "userId": "AIDACKCEVSQ6C2EXAMPLE", + "appId": null, + "clusterId": null, + "bodySize": 80 + }, + "redelivered": false, + "data": "ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ==" + } + ] + } +} diff --git a/powertools-validation/src/test/resources/scheduled_event.json b/powertools-validation/src/test/resources/scheduled_event.json new file mode 100644 index 000000000..12d18ba5d --- /dev/null +++ b/powertools-validation/src/test/resources/scheduled_event.json @@ -0,0 +1,14 @@ +{ + "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", + "source": "aws.events", + "account": "123456789012", + "region": "eu-central-1", + "resources": [ + "arn:aws:events:eu-central-1:123456789012:rule/my-schedule" + ], + "detail": { + "id": 1234, + "name": "product", + "price": 42 + } +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/schema_v4.json b/powertools-validation/src/test/resources/schema_v4.json index ae277d476..3cd310a53 100644 --- a/powertools-validation/src/test/resources/schema_v4.json +++ b/powertools-validation/src/test/resources/schema_v4.json @@ -18,5 +18,9 @@ "exclusiveMinimum": true } }, - "required": ["id", "name", "price"] + "required": [ + "id", + "name", + "price" + ] } \ No newline at end of file diff --git a/powertools-validation/src/test/resources/sns_event.json b/powertools-validation/src/test/resources/sns_event.json new file mode 100644 index 000000000..81f6a4795 --- /dev/null +++ b/powertools-validation/src/test/resources/sns_event.json @@ -0,0 +1,26 @@ +{ + "records": [ + { + "eventSource": "aws:sns", + "eventVersion": "1.0", + "eventSubscriptionArn": "arn:aws:sns:eu-central-1:123456789012:TopicSendToMe:e3ddc7d5-2f86-40b8-a13d-3362f94fd8dd", + "sns": { + "type": "Notification", + "messageId": "dc918f50-80c6-56a2-ba33-d8a9bbf013ab", + "topicArn": "arn:aws:sns:eu-central-1:123456789012:TopicSendToMe", + "subject": "Test sns message", + "message": "{\"id\":1234, \"name\":\"product\", \"price\":42}", + "signatureVersion": "1", + "signature": "UWnPpkqPAphyr+6PXzUF9++4zJcw==", + "signingCertUrl": "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-a86cb10b4e1f29c941702d737128f7b6.pem", + "unsubscribeUrl": "https://sns.eu-central-1.amazonaws.com/?Action=Unsubscribe", + "messageAttributes": { + "name": { + "type": "String", + "value": "Bob" + } + } + } + } + ] +} \ No newline at end of file diff --git a/powertools-validation/src/test/resources/sqs.json b/powertools-validation/src/test/resources/sqs.json index 129e79bb2..10a2bbbbf 100644 --- a/powertools-validation/src/test/resources/sqs.json +++ b/powertools-validation/src/test/resources/sqs.json @@ -11,7 +11,6 @@ "ApproximateFirstReceiveTimestamp": "1601975709499" }, "messageAttributes": { - }, "md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96", "eventSource": "aws:sqs", diff --git a/powertools-validation/src/test/resources/sqs_message.json b/powertools-validation/src/test/resources/sqs_message.json index 068279b74..878b402df 100644 --- a/powertools-validation/src/test/resources/sqs_message.json +++ b/powertools-validation/src/test/resources/sqs_message.json @@ -11,7 +11,6 @@ "ApproximateFirstReceiveTimestamp": "1601975709499" }, "messageAttributes": { - }, "md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96", "eventSource": "aws:sqs", From ba8075f291a623bd42b422b9edb96572ac0a6f0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 13:29:24 +0200 Subject: [PATCH 0371/1008] build(deps): bump aws.sdk.version from 2.20.101 to 2.20.102 (#1279) Bumps `aws.sdk.version` from 2.20.101 to 2.20.102. Updates `software.amazon.awssdk:bom` from 2.20.101 to 2.20.102 Updates `http-client-spi` from 2.20.101 to 2.20.102 Updates `url-connection-client` from 2.20.101 to 2.20.102 Updates `s3` from 2.20.101 to 2.20.102 Updates `lambda` from 2.20.101 to 2.20.102 Updates `cloudwatch` from 2.20.101 to 2.20.102 Updates `xray` from 2.20.101 to 2.20.102 Updates `cloudformation` from 2.20.101 to 2.20.102 Updates `sts` from 2.20.101 to 2.20.102 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 314a8c32b..7b22ab33e 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.101</version> + <version>2.20.102</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index b7aaa1e90..916bc7fe2 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.101</aws.sdk.version> + <aws.sdk.version>2.20.102</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 5ba9cf43695450b4f168a0cb6520e887fc8b720d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 11 Jul 2023 17:29:00 +0200 Subject: [PATCH 0372/1008] docs: update documentation for aspectJ (#1273) * Update documentation for aspectJ * update README --- README.md | 96 +++++++++- docs/core/logging.md | 135 +++++++++++++ docs/core/metrics.md | 133 +++++++++++++ docs/core/tracing.md | 137 +++++++++++++- docs/index.md | 189 ++++++++++++++++--- docs/utilities/batch.md | 103 ++++++++-- docs/utilities/idempotency.md | 97 +++++++++- docs/utilities/parameters.md | 141 ++++++++++++-- docs/utilities/serialization.md | 16 +- docs/utilities/sqs_large_message_handling.md | 103 ++++++++-- docs/utilities/validation.md | 117 +++++++++--- mkdocs.yml | 2 +- 12 files changed, 1142 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index fe6e9afba..d1f635a9c 100644 --- a/README.md +++ b/README.md @@ -13,24 +13,24 @@ Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it -* [maven](https://maven.apache.org/): +#### Maven: ```xml <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.0</version> </dependency> ... </dependencies> @@ -38,6 +38,7 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project: +For Java 11+, use the following: ```xml <build> <plugins> @@ -78,6 +79,93 @@ And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambd </build> ``` +For Java 8, use the following: +```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> +``` +#### gradle + +For Java 11+: + + ```groovy + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + } + + 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 = 11 + targetCompatibility = 11 + ``` + +For Java8: + + ```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 + ``` + + ## Example See the **[examples](examples)** directory for example projects showcasing usage of different utilities. diff --git a/docs/core/logging.md b/docs/core/logging.md index 1af206314..bf5fb6767 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -11,6 +11,141 @@ Logging provides an opinionated logger with output structured 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 +## 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-logging</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-logging</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-logging</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-logging</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-logging:{{ powertools.version }}' + } + + sourceCompatibility = 11 + targetCompatibility = 11 + ``` + +=== "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-logging:{{ powertools.version }}' + } + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + ``` + + ## Initialization 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. diff --git a/docs/core/metrics.md b/docs/core/metrics.md index f84508669..e06ab6d10 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -26,6 +26,139 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar <figcaption>Metric terminology, visually explained</figcaption> </figure> +## 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-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>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-metrics</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-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+" + + ```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-metrics:{{ powertools.version }}' + } + + sourceCompatibility = 11 + targetCompatibility = 11 + ``` + +=== "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-metrics:{{ powertools.version }}' + } + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + ``` ## Getting started diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 13d0be292..17e81b867 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -15,7 +15,142 @@ a provides functionality to reduce the overhead of performing common tracing tas * Better developer experience when developing with multiple threads. * Auto patch supported modules by AWS X-Ray -Initialization +## 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-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>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-tracing</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-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" + + ```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-tracing:{{ powertools.version }}' + } + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + ``` + + +## 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. diff --git a/docs/index.md b/docs/index.md index 4358b08f4..f00e0314d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,24 +28,63 @@ This project separates core utilities that will be available in other runtimes v ## Install -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) - -**Quick hello world examples using SAM CLI** +**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). ```bash - sam init --location gh:aws-samples/cookiecutter-aws-sam-powertools-java +sam init + +Which template source would you like to use? + 1 - AWS Quick Start Templates + 2 - Custom Template Location +Choice: 1 + +Choose an AWS Quick Start application template + 1 - Hello World Example + 2 - Data processing + 3 - Hello World Example with Powertools for AWS Lambda + 4 - Multi-step workflow + 5 - Scheduled task + 6 - Standalone function + 7 - Serverless API + 8 - Infrastructure event management + 9 - Lambda Response Streaming + 10 - Serverless Connector Hello World Example + 11 - Multi-step workflow with Connectors + 12 - Full Stack + 13 - Lambda EFS example + 14 - DynamoDB Example + 15 - Machine Learning +Template: 3 + +Which runtime would you like to use? + 1 - dotnet6 + 2 - java17 + 3 - java11 + 4 - java8.al2 + 5 - java8 + 6 - nodejs18.x + 7 - nodejs16.x + 8 - nodejs14.x + 9 - python3.9 + 10 - python3.8 + 11 - python3.7 + 12 - python3.10 +Runtime: 2, 3, 4 or 5 ``` -For more information about the project and available options refer to this [repository](https://github.com/aws-samples/cookiecutter-aws-sam-powertools-java/blob/main/README.md) +**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" +* [Maven](https://maven.apache.org/) +* [Gradle](https://gradle.org) - ```xml hl_lines="3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55" +Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. + +=== "Maven Java 11+" + + ```xml <dependencies> ... <dependency> @@ -74,6 +113,69 @@ For more information about the project and available options refer to this [repo <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-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> + ``` + +=== "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> @@ -106,30 +208,57 @@ For more information about the project and available options refer to this [repo </build> ``` -=== "Gradle" +=== "Gradle Java 11+" ```groovy - plugins{ - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' - } - - 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 }}' -// This dependency is needed for Java17+, please uncomment it if you are using Java17+ -// implementation 'org.aspectj:aspectjrt:1.9.19' - } - - sourceCompatibility = 11 - targetCompatibility = 11 + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + } + + 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 = 11 + targetCompatibility = 11 ``` +=== "Gradle Java 1.8" + + ```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 + ``` + +???+ tip "Why a different configuration?" + Lambda Powertools for 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+. + ## Environment variables !!! info diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 0a2add081..c2072105d 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -23,10 +23,11 @@ are returned to the queue. ## Install -To install this utility, add the following dependency to your project. +Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. -=== "Maven" - ```xml hl_lines="3 4 5 6 7 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36" +=== "Maven Java 11+" + + ```xml hl_lines="3-7 16 18 24-27"" <dependencies> ... <dependency> @@ -36,6 +37,7 @@ To install this utility, add the following dependency to your project. </dependency> ... </dependencies> + ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> <build> <plugins> @@ -44,6 +46,51 @@ To install this utility, add the following dependency to your project. <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> @@ -68,24 +115,44 @@ To install this utility, add the following dependency to your project. </build> ``` -=== "Gradle" +=== "Gradle Java 11+" - ```groovy - plugins{ - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' - } + ```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 + ``` - repositories { - mavenCentral() - } +=== "Gradle Java 1.8" - dependencies { - ... - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' -// This dependency is needed for Java17+, please uncomment it if you are using Java17+ -// implementation 'org.aspectj:aspectjrt:1.9.19' - } + ```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 diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 2f4774c94..5392b8d4c 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -26,8 +26,11 @@ times with the same parameters**. This makes idempotent operations safe to retry ## Getting started ### Installation -=== "Maven" - ```xml hl_lines="3-7 24-27" +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> @@ -37,7 +40,7 @@ times with the same parameters**. This makes idempotent operations safe to retry </dependency> ... </dependencies> - + ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> <build> <plugins> @@ -46,6 +49,51 @@ times with the same parameters**. This makes idempotent operations safe to retry <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-idempotency</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-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> @@ -55,7 +103,6 @@ times with the same parameters**. This makes idempotent operations safe to retry <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> </aspectLibrary> - ... </aspectLibraries> </configuration> <executions> @@ -71,6 +118,46 @@ times with the same parameters**. This makes idempotent operations safe to retry </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-idempotency:{{ 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. @@ -273,7 +360,7 @@ Imagine the function executes successfully, but the client never receives the re !!! warning "Warning: Idempotency for JSON payloads" The payload extracted by the `EventKeyJMESPath` is treated as a string by default, so will be sensitive to differences in whitespace even when the JSON payload itself is identical. - To alter this behaviour, you can use the [JMESPath built-in function](utilities.md#powertools_json-function) `powertools_json()` to treat the payload as a JSON object rather than a string. + 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" diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 7e70248d4..85d30d77e 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -16,27 +16,136 @@ It also provides a base class to create your parameter provider implementation. * Transform parameter values from JSON or base 64 encoded strings ## Install +Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. -To install this utility, add the following dependency to your project. +=== "Maven Java 11+" -=== "Maven" - - ```xml - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - <version>{{ powertools.version }}</version> - </dependency> + ```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>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-parameters</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + ... + </plugins> + </build> ``` -=== "Gradle" - ```groovy - dependencies { +=== "Maven Java 1.8" + + ```xml hl_lines="3-7 16 18 24-27" + <dependencies> ... - aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' -// This dependency is needed for Java17+, please uncomment it if you are using Java17+ -// implementation 'org.aspectj:aspectjrt:1.9.19' - } + <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+" + + ```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-parameters:{{ 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-parameters:{{ powertools.version }}' + } + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 ``` **IAM Permissions** diff --git a/docs/utilities/serialization.md b/docs/utilities/serialization.md index bd654393f..b47bdbd91 100644 --- a/docs/utilities/serialization.md +++ b/docs/utilities/serialization.md @@ -234,14 +234,14 @@ It can also handle a collection of elements like the records of an SQS event: | `APIGatewayV2HTTPEvent` | `body` | | | `SNSEvent` | `Records[0].Sns.Message` | | | `SQSEvent` | `Records[*].body` | x | - | `ScheduledEvent` | `detail` | | - | `ApplicationLoadBalancerRequestEvent` | `body` | | - | `CloudWatchLogsEvent` | `powertools_base64_gzip(data)` | | - | `CloudFormationCustomResourceEvent` | `resourceProperties` | | - | `KinesisEvent` | `Records[*].kinesis.powertools_base64(data)` | x | - | `KinesisFirehoseEvent` | `Records[*].powertools_base64(data)` | x | - | `KafkaEvent` | `records[*].values[*].powertools_base64(value)` | x | - | `ActiveMQEvent` | `messages[*].powertools_base64(data)` | x | +| `ScheduledEvent` | `detail` | | +| `ApplicationLoadBalancerRequestEvent` | `body` | | +| `CloudWatchLogsEvent` | `powertools_base64_gzip(data)` | | +| `CloudFormationCustomResourceEvent` | `resourceProperties` | | +| `KinesisEvent` | `Records[*].kinesis.powertools_base64(data)` | x | +| `KinesisFirehoseEvent` | `Records[*].powertools_base64(data)` | x | +| `KafkaEvent` | `records[*].values[*].powertools_base64(value)` | x | +| `ActiveMQEvent` | `messages[*].powertools_base64(data)` | x | | `RabbitMQEvent` | `rmqMessagesByQueue[*].values[*].powertools_base64(data)` | x | | `KinesisAnalyticsFirehoseInputPreprocessingEvent` | `Records[*].kinesis.powertools_base64(data)` | x | | `KinesisAnalyticsStreamsInputPreprocessingEvent` | `Records[*].kinesis.powertools_base64(data)` | x | diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md index 5f7ede095..82e1afef4 100644 --- a/docs/utilities/sqs_large_message_handling.md +++ b/docs/utilities/sqs_large_message_handling.md @@ -30,11 +30,10 @@ This utility is compatible with versions *[1.1.0+](https://github.com/awslabs/am ``` ## Install +Depending on your version of Java (either Java 1.8 or 11+), the configuration slightly changes. -To install this utility, add the following dependency to your project. - -=== "Maven" - ```xml hl_lines="3 4 5 6 7 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36" +=== "Maven Java 11+" + ```xml hl_lines="3-7 16 18 24-27" <dependencies> ... <dependency> @@ -44,6 +43,7 @@ To install this utility, add the following dependency to your project. </dependency> ... </dependencies> + ... <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> <build> <plugins> @@ -52,6 +52,51 @@ To install this utility, add the following dependency to your project. <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> @@ -76,24 +121,44 @@ To install this utility, add the following dependency to your project. </build> ``` -=== "Gradle" +=== "Gradle Java 11+" - ```groovy - plugins{ - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' - } + ```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 + ``` - repositories { - mavenCentral() - } +=== "Gradle Java 1.8" - dependencies { - ... - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' -// This dependency is needed for Java17+, please uncomment it if you are using Java17+ -// implementation 'org.aspectj:aspectjrt:1.9.19' - } + ```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 diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 8733c0e77..928ffb6c8 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -12,20 +12,20 @@ This utility provides JSON Schema validation for payloads held within events and * 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. -To install this utility, add the following dependency to your project. - -=== "Maven" - ```xml hl_lines="3 4 5 6 7 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36" +=== "Maven Java 11+" + ```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> - ... + ... + <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> @@ -34,6 +34,51 @@ To install this utility, add the following dependency to your project. <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-validation</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-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> @@ -58,25 +103,47 @@ To install this utility, add the following dependency to your project. </build> ``` -=== "Gradle" +=== "Gradle Java 11+" - ```groovy - plugins{ - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.3.0' - } + ```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-validation:{{ powertools.version }}' + } + + sourceCompatibility = 11 // or higher + targetCompatibility = 11 // or higher + ``` - repositories { - mavenCentral() - } +=== "Gradle Java 1.8" - dependencies { - aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' -// This dependency is needed for Java17+, please uncomment it if you are using Java17+ -// implementation 'org.aspectj:aspectjrt:1.9.19' - } + ```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 ``` + ## Validating events You can validate inbound and outbound events using `@Validation` annotation. diff --git a/mkdocs.yml b/mkdocs.yml index b758239de..5fd3206b0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,7 +83,7 @@ extra_javascript: extra: powertools: - version: 1.17.0-SNAPSHOT + version: 1.16.0 # to update after each release (we do not want snapshot version here) repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs From a19def3e4ea9e510c0ce4ad602abe58db0610d1f Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 11 Jul 2023 17:39:45 +0200 Subject: [PATCH 0373/1008] fix: Handle batch failures in FIFO queues correctly (#1183) --- docs/utilities/batch.md | 10 +- powertools-sqs/pom.xml | 4 + .../lambda/powertools/sqs/SqsUtils.java | 45 ++++- ...ippedMessageDueToFailedBatchException.java | 12 ++ .../sqs/SqsUtilsFifoBatchProcessorTest.java | 166 ++++++++++++++++++ .../src/test/resources/SqsFifoBatchEvent.json | 73 ++++++++ 6 files changed, 305 insertions(+), 5 deletions(-) create mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java create mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java create mode 100644 powertools-sqs/src/test/resources/SqsFifoBatchEvent.json diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index c2072105d..95704b8a0 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -4,6 +4,9 @@ description: Utility --- 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** @@ -177,8 +180,11 @@ Both have nearly the same behaviour when it comes to processing messages from th * **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, 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`. - - **3)** Raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue + - **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. diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 5eafcc8d3..618aa948c 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -45,6 +45,10 @@ <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> 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 index 3461c6755..9fff4dc6f 100644 --- 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 @@ -15,7 +15,9 @@ import java.lang.reflect.Constructor; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import java.util.function.Function; import java.util.stream.Collectors; @@ -26,6 +28,7 @@ import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.lambda.powertools.sqs.exception.SkippedMessageDueToFailedBatchException; import software.amazon.lambda.powertools.sqs.internal.BatchContext; import software.amazon.payloadoffloading.PayloadS3Pointer; import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; @@ -43,6 +46,9 @@ public final class SqsUtils { private static SqsClient client; private static S3Client s3Client; + // The attribute on an SQS-FIFO message used to record the message group ID + private static final String MESSAGE_GROUP_ID = "MessageGroupId"; + private SqsUtils() { } @@ -490,19 +496,52 @@ public static <R> List<R> batchProcessor(final SQSEvent event, } BatchContext batchContext = new BatchContext(client); - - for (SQSMessage message : event.getRecords()) { + 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); } } - batchContext.processSuccessAndHandleFailed(handlerReturn, suppressException, deleteNonRetryableMessageFromQueue, nonRetryableExceptions); + // 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; } diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java new file mode 100644 index 000000000..9dbb66509 --- /dev/null +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java @@ -0,0 +1,12 @@ +package software.amazon.lambda.powertools.sqs.exception; + +/** + * Indicates that a message has been skipped due to the batch it is + * within failing. + */ +public class SkippedMessageDueToFailedBatchException extends Exception { + + public SkippedMessageDueToFailedBatchException() { + } + +} 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 new file mode 100644 index 000000000..cfa79dc36 --- /dev/null +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java @@ -0,0 +1,166 @@ +package software.amazon.lambda.powertools.sqs; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.tests.EventLoader; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.*; +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; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.MockitoAnnotations.openMocks; +import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; +import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; + +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/resources/SqsFifoBatchEvent.json b/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json new file mode 100644 index 000000000..726e45ea1 --- /dev/null +++ b/powertools-sqs/src/test/resources/SqsFifoBatchEvent.json @@ -0,0 +1,73 @@ +{ + "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 From b6b118f23025a5dd66ffe76641cda170b80105cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 12 Jul 2023 17:18:13 +0200 Subject: [PATCH 0374/1008] fix: ParamManager cannot provide default SSM & Secrets providers (#1282) * fix issue 1280 * Update powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java missing issue number --- powertools-parameters/pom.xml | 14 ++++++ .../parameters/DynamoDbProvider.java | 43 +++++++++++------- .../powertools/parameters/ParamManager.java | 9 +++- .../powertools/parameters/SSMProvider.java | 44 ++++++++++++------- .../parameters/SecretsProvider.java | 42 +++++++++++------- .../parameters/ParamManagerTest.java | 20 ++++++++- 6 files changed, 120 insertions(+), 52 deletions(-) diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index af980a4c3..b50844d99 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -133,4 +133,18 @@ </dependency> </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <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/DynamoDbProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java index 5144af0c2..1b77aed88 100644 --- 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 @@ -5,11 +5,13 @@ 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.DynamoDbClientBuilder; 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.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @@ -18,6 +20,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; + /** * Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table * is described in the Powertools for AWS Lambda (Java) documentation. @@ -30,23 +34,16 @@ public class DynamoDbProvider extends BaseProvider { private final DynamoDbClient client; private final String tableName; - public DynamoDbProvider(CacheManager cacheManager, String tableName) { - this(cacheManager, DynamoDbClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(), - 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); + } + /** * Return a single value from the DynamoDB parameter provider. * @@ -136,11 +133,11 @@ public DynamoDbProvider build() { throw new IllegalStateException("No DynamoDB table name provided; please provide one"); } DynamoDbProvider provider; - if (client != null) { - provider = new DynamoDbProvider(cacheManager, client, table); - } else { - provider = new DynamoDbProvider(cacheManager, table); + if (client == null) { + client = createClient(); } + provider = new DynamoDbProvider(cacheManager, client, table); + if (transformationManager != null) { provider.setTransformationManager(transformationManager); } @@ -191,5 +188,21 @@ public DynamoDbProvider.Builder withTransformationManager(TransformationManager this.transformationManager = transformationManager; return this; } + + private static DynamoDbClient createClient() { + DynamoDbClientBuilder dynamoDbClientBuilder = DynamoDbClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { + dynamoDbClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + + return dynamoDbClientBuilder.build(); + } } } 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 index b2e541c43..c8abedf06 100644 --- 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 @@ -37,14 +37,19 @@ public final class ParamManager { /** * Get a concrete implementation of {@link BaseProvider}.<br/> - * You can specify {@link SecretsProvider}, {@link SSMProvider}, {@link DynamoDbProvider}, or create your + * 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. + * @deprecated You should not use this method directly but a typed one (getSecretsProvider, getSsmProvider, getDynamoDbProvider, getAppConfigProvider), will be removed in v2 * @return a {@link SecretsProvider} */ + // 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); } @@ -166,7 +171,7 @@ public static TransformationManager getTransformationManager() { static <T extends BaseProvider> T createProvider(Class<T> providerClass) { try { Constructor<T> constructor = providerClass.getDeclaredConstructor(CacheManager.class); - T provider = constructor.newInstance(cacheManager); + T provider = constructor.newInstance(cacheManager); // FIXME: avoid reflection here as we may have issues (#1280) provider.setTransformationManager(transformationManager); return provider; } catch (ReflectiveOperationException e) { diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index d6d87747b..2eb2d4199 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -74,8 +74,7 @@ */ public class SSMProvider extends BaseProvider { - private SsmClient client; - + private final SsmClient client; private boolean decrypt = false; private boolean recursive = false; @@ -93,12 +92,13 @@ public class SSMProvider extends BaseProvider { } /** - * Constructor + * Constructor with only a CacheManager<br/> * + * Used in {@link ParamManager#createProvider(Class)} * @param cacheManager handles the parameter caching */ SSMProvider(CacheManager cacheManager) { - super(cacheManager); + this(cacheManager, Builder.createClient()); } /** @@ -228,6 +228,11 @@ protected void resetToDefaults() { decrypt = false; } + // For tests purpose only + SsmClient getClient() { + return client; + } + /** * Create a builder that can be used to configure and create a {@link SSMProvider}. * @@ -237,6 +242,7 @@ public static SSMProvider.Builder builder() { return new SSMProvider.Builder(); } + static class Builder { private SsmClient client; private CacheManager cacheManager; @@ -253,19 +259,7 @@ public SSMProvider build() { } SSMProvider provider; if (client == null) { - SsmClientBuilder ssmClientBuilder = SsmClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { - ssmClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - client = ssmClientBuilder.build(); + client = createClient(); } provider = new SSMProvider(cacheManager, client); @@ -288,6 +282,22 @@ public SSMProvider.Builder withClient(SsmClient client) { return this; } + private static SsmClient createClient() { + SsmClientBuilder ssmClientBuilder = SsmClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { + ssmClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + + return ssmClientBuilder.build(); + } + /** * <b>Mandatory</b>. Provide a CacheManager to the {@link SSMProvider} * 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 index 54d0daee3..ea8b5a9d0 100644 --- 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 @@ -58,7 +58,7 @@ */ public class SecretsProvider extends BaseProvider { - private SecretsManagerClient client; + private final SecretsManagerClient client; /** * Constructor with custom {@link SecretsManagerClient}. <br/> @@ -74,12 +74,13 @@ public class SecretsProvider extends BaseProvider { } /** - * Constructor + * Constructor with only a CacheManager<br/> * + * Used in {@link ParamManager#createProvider(Class)} * @param cacheManager handles the parameter caching */ SecretsProvider(CacheManager cacheManager) { - super(cacheManager); + this(cacheManager, Builder.createClient()); } /** @@ -135,6 +136,11 @@ public SecretsProvider withTransformation(Class<? extends Transformer> transform return this; } + // For test purpose only + SecretsManagerClient getClient() { + return client; + } + /** * Create a builder that can be used to configure and create a {@link SecretsProvider}. * @@ -161,19 +167,7 @@ public SecretsProvider build() { } SecretsProvider provider; if (client == null) { - SecretsManagerClientBuilder secretsManagerClientBuilder = SecretsManagerClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { - secretsManagerClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - client = secretsManagerClientBuilder.build(); + client = createClient(); } provider = new SecretsProvider(cacheManager, client); @@ -196,6 +190,22 @@ public Builder withClient(SecretsManagerClient client) { return this; } + private static SecretsManagerClient createClient() { + SecretsManagerClientBuilder secretsManagerClientBuilder = SecretsManagerClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); + + // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start + // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set + // fall back to the default provider chain if the mode is anything other than on-demand. + String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); + if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { + secretsManagerClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); + } + + return secretsManagerClientBuilder.build(); + } + /** * <b>Mandatory</b>. Provide a CacheManager to the {@link SecretsProvider} * 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 index ee61691c1..a21a6082c 100644 --- 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 @@ -18,6 +18,7 @@ import software.amazon.lambda.powertools.parameters.internal.CustomProvider; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +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; @@ -79,23 +80,38 @@ public void testGetProviderWithProviderClass_throwsException() { } @Test - public void testGetSecretsProvider() { + public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { // Act SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); // Assert assertNotNull(secretsProvider); + assertNotNull(secretsProvider.getClient()); } @Test - public void testGetSSMProvider() { + 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)); + } } From f19e202d21239be4a3a5b3ba72424d345dc133ad Mon Sep 17 00:00:00 2001 From: Mark Sailes <45629314+msailes@users.noreply.github.com> Date: Thu, 13 Jul 2023 21:56:38 +0100 Subject: [PATCH 0375/1008] docs: adding our customer references (#1287) --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index d1f635a9c..d6312d348 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,18 @@ See the **[examples](examples)** directory for example projects showcasing usag 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). +## How to support Powertools for AWS Lambda (Java)? + +### 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. + +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/) From a7e3782d31715134153ea88b0da827efc1c83cdc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:31:31 +0200 Subject: [PATCH 0376/1008] build(deps): bump aws.sdk.version from 2.20.102 to 2.20.103 (#1288) Bumps `aws.sdk.version` from 2.20.102 to 2.20.103. Updates `software.amazon.awssdk:bom` from 2.20.102 to 2.20.103 Updates `http-client-spi` from 2.20.102 to 2.20.103 Updates `url-connection-client` from 2.20.102 to 2.20.103 Updates `s3` from 2.20.102 to 2.20.103 Updates `lambda` from 2.20.102 to 2.20.103 Updates `cloudwatch` from 2.20.102 to 2.20.103 Updates `xray` from 2.20.102 to 2.20.103 Updates `cloudformation` from 2.20.102 to 2.20.103 Updates `sts` from 2.20.102 to 2.20.103 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 7b22ab33e..c6de62914 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.102</version> + <version>2.20.103</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 916bc7fe2..2e68a99ec 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.102</aws.sdk.version> + <aws.sdk.version>2.20.103</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From ba190dcbf7814fb0719d3541f466257529cb2e8e Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 17 Jul 2023 10:31:44 +0200 Subject: [PATCH 0377/1008] fix: idempotency timeout bug (#1285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Idemp should be fixed, test isnt * Fix some of the tests. Need to go through the rest still * Add some docs * Update powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Address review comment --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- .../idempotency/persistence/DataRecord.java | 21 +++++++++++++++++++ .../persistence/DynamoDBPersistenceStore.java | 16 +++++++++++++- .../DynamoDBPersistenceStoreTest.java | 12 +++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java index 934ec3d09..54001c449 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -26,9 +26,30 @@ public class DataRecord { private final String idempotencyKey; private final String status; + + /** + * This field is controlling how long the result of the idempotent + * event is cached. It is stored in _seconds since epoch_. + * + * DynamoDB's TTL mechanism is used to remove the record once the + * expiry has been reached, and subsequent execution of the request + * will be permitted. The user must configure this on their table. + */ private final long expiryTimestamp; private final String responseData; private final String payloadHash; + + /** + * The in-progress field is set to the remaining lambda execution time + * when the record is created. + * This field is stored in _milliseconds since epoch_. + * + * This ensures that: + * + * 1/ other concurrently executing requests are blocked from starting + * 2/ if a lambda times out, subsequent requests will be allowed again, despite + * the fact that the idempotency record is already in the table + */ private final OptionalLong inProgressExpiryTimestamp; public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash) { diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 47ddf4c5c..6b5d0fcb2 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -126,6 +126,19 @@ public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoun return itemToRecord(response.item()); } + /** + * Store's the given idempotency record in the DDB store. If there + * is an existing record that has expired - either due to the + * cache expiry or due to the in_progress_expiry - the record + * will be overwritten and the idempotent operation can continue. + * + * <b>Note: This method writes only expiry and status information - not + * the results of the operation itself.</b> + * + * @param record DataRecord instance to store + * @param now + * @throws IdempotencyItemAlreadyExistsException + */ @Override public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlreadyExistsException { Map<String, AttributeValue> item = new HashMap<>(getKey(record.getIdempotencyKey())); @@ -152,6 +165,7 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre Map<String, AttributeValue> expressionAttributeValues = Stream.of( new AbstractMap.SimpleEntry<>(":now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()), + new AbstractMap.SimpleEntry<>(":now_milliseconds", AttributeValue.builder().n(String.valueOf(now.toEpochMilli())).build()), new AbstractMap.SimpleEntry<>(":inprogress", AttributeValue.builder().s(INPROGRESS.toString()).build()) ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -160,7 +174,7 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre PutItemRequest.builder() .tableName(tableName) .item(item) - .conditionExpression("attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now AND #status = :inprogress)") + .conditionExpression("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() diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java index 86e35cd33..768da2eaa 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java @@ -95,7 +95,7 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfLambdaWasInProgressAndTimed Map<String, AttributeValue> item = new HashMap<>(key); Instant now = Instant.now(); long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); - long progressExpiry = now.minus(30, ChronoUnit.SECONDS).getEpochSecond(); + long progressExpiry = now.minus(30, ChronoUnit.SECONDS).toEpochMilli(); item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); item.put("data", AttributeValue.builder().s("Fake Data").build()); @@ -152,14 +152,14 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA } @Test - public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordAlreadyExistAndProgressNotExpiredAfterLambdaTimedOut() { + public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpiredAfterLambdaTimedOut() { key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); // GIVEN: Insert a fake item with same id Map<String, AttributeValue> item = new HashMap<>(key); Instant now = Instant.now(); long expiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); // not expired - long progressExpiry = now.plus(30, ChronoUnit.SECONDS).getEpochSecond(); // not expired + long progressExpiry = now.plus(30, ChronoUnit.SECONDS).toEpochMilli(); // not expired item.put("expiration", AttributeValue.builder().n(String.valueOf(expiry)).build()); item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); item.put("data", AttributeValue.builder().s("Fake Data").build()); @@ -172,10 +172,10 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA new DataRecord("key", DataRecord.Status.INPROGRESS, expiry2, - null, + "Fake Data 2", null - ), now) - ).isInstanceOf(IdempotencyItemAlreadyExistsException.class); + ), now)) + .isInstanceOf(IdempotencyItemAlreadyExistsException.class); // THEN: item was not updated, retrieve the initial one Map<String, AttributeValue> itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); From 903d08b09e7b65316a073b5f5d8585e4adb36fb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:01:59 +0200 Subject: [PATCH 0378/1008] build(deps): bump aws.sdk.version from 2.20.103 to 2.20.104 (#1292) Bumps `aws.sdk.version` from 2.20.103 to 2.20.104. Updates `software.amazon.awssdk:bom` from 2.20.103 to 2.20.104 Updates `http-client-spi` from 2.20.103 to 2.20.104 Updates `url-connection-client` from 2.20.103 to 2.20.104 Updates `s3` from 2.20.103 to 2.20.104 Updates `lambda` from 2.20.103 to 2.20.104 Updates `cloudwatch` from 2.20.103 to 2.20.104 Updates `xray` from 2.20.103 to 2.20.104 Updates `cloudformation` from 2.20.103 to 2.20.104 Updates `sts` from 2.20.103 to 2.20.104 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index c6de62914..c5b811678 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.103</version> + <version>2.20.104</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 2e68a99ec..f0c671883 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.103</aws.sdk.version> + <aws.sdk.version>2.20.104</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 5b1c2c43d60b637b26d469ad678de1e3c907b03e Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 19 Jul 2023 09:49:34 +0200 Subject: [PATCH 0379/1008] docs: update README.md (#1294) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md * Update README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- README.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d6312d348..0a7cd0112 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,11 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your </dependencies> ``` -And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project: +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. -For Java 11+, use the following: +<details> + <summary><b>Maven - Java 11 and newer</b></summary> + ```xml <build> <plugins> @@ -78,8 +80,11 @@ For Java 11+, use the following: </plugins> </build> ``` +</details> -For Java 8, use the following: +<details> +<summary><b>Maven - Java 8</b></summary> + ```xml <build> <plugins> @@ -119,11 +124,12 @@ For Java 8, use the following: </plugins> </build> ``` -#### gradle +</details> -For Java 11+: +<details> +<summary><b>Gradle - Java 11+</b></summary> - ```groovy +```groovy plugins { id 'java' id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' @@ -141,11 +147,13 @@ For Java 11+: sourceCompatibility = 11 targetCompatibility = 11 - ``` +``` +</details> -For Java8: +<details> +<summary><b>Gradle - Java 8</b></summary> - ```groovy +```groovy plugins { id 'java' id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' @@ -163,10 +171,10 @@ For Java8: sourceCompatibility = 1.8 targetCompatibility = 1.8 - ``` - +``` +</details> -## Example +## Examples See the **[examples](examples)** directory for example projects showcasing usage of different utilities. From 8da24cb175815418b0a4906c1ef9785b01394210 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <info@michelericciardi.co.uk> Date: Wed, 19 Jul 2023 09:50:15 +0200 Subject: [PATCH 0380/1008] build(examples): Add example for the powertools-cloudformation module (#1089) --- examples/pom.xml | 1 + .../README.md | 40 ++++ .../infra/cdk/.gitignore | 13 ++ .../infra/cdk/cdk.json | 37 +++ .../infra/cdk/pom.xml | 53 +++++ ...owertoolsExamplesCloudformationCdkApp.java | 16 ++ ...ertoolsExamplesCloudformationCdkStack.java | 89 +++++++ .../infra/sam/events/create_event.json | 12 + .../infra/sam/events/delete_event.json | 14 ++ .../infra/sam/events/update_event.json | 14 ++ .../infra/sam/template.yaml | 50 ++++ .../pom.xml | 219 ++++++++++++++++++ .../src/main/java/helloworld/App.java | 164 +++++++++++++ .../src/main/resources/log4j2.xml | 17 ++ 14 files changed, 739 insertions(+) create mode 100644 examples/powertools-examples-cloudformation/README.md create mode 100644 examples/powertools-examples-cloudformation/infra/cdk/.gitignore create mode 100644 examples/powertools-examples-cloudformation/infra/cdk/cdk.json create mode 100644 examples/powertools-examples-cloudformation/infra/cdk/pom.xml create mode 100644 examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java create mode 100644 examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java create mode 100644 examples/powertools-examples-cloudformation/infra/sam/events/create_event.json create mode 100644 examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json create mode 100644 examples/powertools-examples-cloudformation/infra/sam/events/update_event.json create mode 100644 examples/powertools-examples-cloudformation/infra/sam/template.yaml create mode 100644 examples/powertools-examples-cloudformation/pom.xml create mode 100644 examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml diff --git a/examples/pom.xml b/examples/pom.xml index cca621163..c9b8ea8ae 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,6 +21,7 @@ <module>powertools-examples-serialization</module> <module>powertools-examples-sqs</module> <module>powertools-examples-validation</module> + <module>powertools-examples-cloudformation</module> </modules> <!-- Don't deploy the examples --> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md new file mode 100644 index 000000000..6dbffcf37 --- /dev/null +++ b/examples/powertools-examples-cloudformation/README.md @@ -0,0 +1,40 @@ +# Cloudformation Custom Resource Example + +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/). + +## Deploy the sample application + +This sample can be used either with the Serverless Application Model (SAM) or with CDK. + +### Deploy with SAM CLI +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java 8 - [Install Java 8](https://docs.aws.amazon.com/corretto/latest/corretto-8-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) + +To build and deploy this application for the first time, run the following in your shell: + +```bash +cd infra/sam +sam build +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-20230717 +``` + +### 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) +* Maven - [Install Maven](https://maven.apache.org/install.html) +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +To build and deploy this application for the first time, run the following in your shell: + +```bash +cd infra/cdk +mvn package +cdk synth +cdk deploy -c BucketNameParam=my-unique-bucket-20230718 +``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/cdk/.gitignore b/examples/powertools-examples-cloudformation/infra/cdk/.gitignore new file mode 100644 index 000000000..1db21f162 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/cdk/.gitignore @@ -0,0 +1,13 @@ +.classpath.txt +target +.classpath +.project +.idea +.settings +.vscode +*.iml + +# CDK asset staging directory +.cdk.staging +cdk.out + diff --git a/examples/powertools-examples-cloudformation/infra/cdk/cdk.json b/examples/powertools-examples-cloudformation/infra/cdk/cdk.json new file mode 100644 index 000000000..fe011b328 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/cdk/cdk.json @@ -0,0 +1,37 @@ +{ + "app": "mvn -e -q compile exec:java", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "target", + "pom.xml", + "src/test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true + } +} diff --git a/examples/powertools-examples-cloudformation/infra/cdk/pom.xml b/examples/powertools-examples-cloudformation/infra/cdk/pom.xml new file mode 100644 index 000000000..30172fa3f --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/cdk/pom.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <groupId>com.myorg</groupId> + <artifactId>powertools-examples-cloudformation-cdk</artifactId> + <version>0.1</version> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <cdk.version>2.59.0</cdk.version> + <constructs.version>[10.0.0,11.0.0)</constructs.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>8</source> + <target>8</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.0.0</version> + <configuration> + <mainClass>com.myorg.PowertoolsExamplesCloudformationCdkApp</mainClass> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <!-- AWS Cloud Development Kit --> + <dependency> + <groupId>software.amazon.awscdk</groupId> + <artifactId>aws-cdk-lib</artifactId> + <version>${cdk.version}</version> + </dependency> + + <dependency> + <groupId>software.constructs</groupId> + <artifactId>constructs</artifactId> + <version>${constructs.version}</version> + </dependency> + </dependencies> +</project> diff --git a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java new file mode 100644 index 000000000..84060171b --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java @@ -0,0 +1,16 @@ +package com.myorg; + +import software.amazon.awscdk.App; +import software.amazon.awscdk.StackProps; + +public class PowertoolsExamplesCloudformationCdkApp { + public static void main(final String[] args) { + App app = new App(); + + new PowertoolsExamplesCloudformationCdkStack(app, "PowertoolsExamplesCloudformationCdkStack", StackProps.builder() + .build()); + + app.synth(); + } +} + diff --git a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java new file mode 100644 index 000000000..e880a3534 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java @@ -0,0 +1,89 @@ +package com.myorg; + +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.*; +import software.amazon.awscdk.services.iam.Effect; +import software.amazon.awscdk.services.iam.PolicyStatement; +import software.amazon.awscdk.services.iam.PolicyStatementProps; +import software.amazon.awscdk.services.lambda.Code; +import software.amazon.awscdk.services.lambda.Function; +import software.amazon.awscdk.services.lambda.FunctionProps; +import software.amazon.awscdk.services.lambda.Runtime; +import software.amazon.awscdk.services.s3.assets.AssetOptions; +import software.constructs.Construct; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +import static java.util.Collections.singletonList; +import static software.amazon.awscdk.BundlingOutput.NOT_ARCHIVED; + +public class PowertoolsExamplesCloudformationCdkStack extends Stack { + + public static final String SAMPLE_BUCKET_NAME = "sample-bucket-name-20230315-abc123"; + + public PowertoolsExamplesCloudformationCdkStack(final Construct scope, final String id) { + this(scope, id, null); + } + + public PowertoolsExamplesCloudformationCdkStack(final Construct scope, final String id, final StackProps props) { + super(scope, id, props); + + + List<String> functionPackagingInstructions = Arrays.asList( + "/bin/sh", + "-c", + "mvn clean install" + + "&& mkdir /asset-output/lib" + + "&& cp target/powertools-examples-cloudformation-*.jar /asset-output/lib" + ); + BundlingOptions bundlingOptions = BundlingOptions.builder() + .command(functionPackagingInstructions) + .image(Runtime.JAVA_11.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(NOT_ARCHIVED) + .build(); + + Function helloWorldFunction = new Function(this, "HelloWorldFunction", FunctionProps.builder() + .runtime(Runtime.JAVA_11) + .code(Code.fromAsset("../../", AssetOptions.builder().bundling(bundlingOptions) + .build())) + .handler("helloworld.App::handleRequest") + .memorySize(512) + .timeout(Duration.seconds(20)) + .environment(Collections + .singletonMap("JAVA_TOOL_OPTIONS", "-XX:+TieredCompilation -XX:TieredStopAtLevel=1")) + .build()); + helloWorldFunction.addToRolePolicy(new PolicyStatement(PolicyStatementProps.builder() + .effect(Effect.ALLOW) + .actions(Arrays.asList("s3:GetLifecycleConfiguration", + "s3:PutLifecycleConfiguration", + "s3:CreateBucket", + "s3:ListBucket", + "s3:DeleteBucket")) + .resources(singletonList("*")).build())); + + String bucketName = (String) this.getNode().tryGetContext("BucketNameParam"); + + Map<String, Serializable> crProperties = new HashMap<>(); + crProperties.put("BucketName", bucketName); + CustomResource.Builder + .create(this, "HelloWorldCustomResource") + .serviceToken(helloWorldFunction.getFunctionArn()) + .properties(crProperties) + .build(); + + } +} diff --git a/examples/powertools-examples-cloudformation/infra/sam/events/create_event.json b/examples/powertools-examples-cloudformation/infra/sam/events/create_event.json new file mode 100644 index 000000000..26ef0a03b --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam/events/create_event.json @@ -0,0 +1,12 @@ +{ + "RequestType": "Create", + "ResponseURL": "http://pre-signed-S3-url-for-response", + "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid", + "RequestId": "unique id for this create request", + "ResourceType": "Custom::TestResource", + "ResourceProperties": { + "BucketName": "test-bucket-20230307-1", + "RetentionDays" : 10, + "StackName": "MyStack" + } +} diff --git a/examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json b/examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json new file mode 100644 index 000000000..d18fdd3e4 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json @@ -0,0 +1,14 @@ +{ + "RequestType": "Delete", + "ResponseURL": "http://pre-signed-S3-url-for-response", + "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid", + "RequestId": "unique id for this create request", + "ResourceType": "Custom::TestResource", + "LogicalResourceId": "MyTestResource", + "PhysicalResourceId": "test-bucket-20230307-1", + "ResourceProperties": { + "BucketName": "test-bucket-20230307-1", + "RetentionDays" : 10, + "StackName": "MyStack" + } +} diff --git a/examples/powertools-examples-cloudformation/infra/sam/events/update_event.json b/examples/powertools-examples-cloudformation/infra/sam/events/update_event.json new file mode 100644 index 000000000..5a5ae2e3f --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam/events/update_event.json @@ -0,0 +1,14 @@ +{ + "RequestType": "Update", + "ResponseURL": "http://pre-signed-S3-url-for-response", + "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid", + "RequestId": "unique id for this create request", + "ResourceType": "Custom::TestResource", + "LogicalResourceId": "MyTestResource", + "PhysicalResourceId": "test-bucket-20230307-1", + "ResourceProperties": { + "BucketName": "test-bucket-20230307-1", + "RetentionDays" : 100, + "StackName": "MyStack" + } +} diff --git a/examples/powertools-examples-cloudformation/infra/sam/template.yaml b/examples/powertools-examples-cloudformation/infra/sam/template.yaml new file mode 100644 index 000000000..a7ce4adf1 --- /dev/null +++ b/examples/powertools-examples-cloudformation/infra/sam/template.yaml @@ -0,0 +1,50 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + powertools-examples-cloudformation + + Sample SAM Template for powertools-examples-cloudformation + +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: java11 + Architectures: + - x86_64 + MemorySize: 512 + Policies: + - Statement: + - Sid: bucketaccess1 + Effect: Allow + Action: + - s3:GetLifecycleConfiguration + - s3:PutLifecycleConfiguration + - s3:CreateBucket + - s3:ListBucket + - s3:DeleteBucket + Resource: '*' + Environment: + Variables: + JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + +Outputs: + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml new file mode 100644 index 000000000..198a85894 --- /dev/null +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -0,0 +1,219 @@ +<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-cloudformation</artifactId> + <packaging>jar</packaging> + + <name>AWS Lambda Powertools for Java library 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.102</aws.sdk.version> + </properties> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>bom</artifactId> + <version>${aws.sdk.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + <version>${lambda.core.version}</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>${lambda.events.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-cloudformation</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</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> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</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>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> + </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> + <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> + </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-cloudformation/src/main/java/helloworld/App.java b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java new file mode 100644 index 000000000..c7744cd5a --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java @@ -0,0 +1,164 @@ +package helloworld; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.waiters.WaiterResponse; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.*; +import software.amazon.awssdk.services.s3.waiters.S3Waiter; +import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; +import software.amazon.lambda.powertools.cloudformation.Response; + +import java.util.Objects; + +/** + * Handler for requests to Lambda function. + */ + +public class App extends AbstractCustomResourceHandler { + private final static Logger log = LogManager.getLogger(App.class); + private final S3Client s3Client; + + public App() { + super(); + s3Client = S3Client.builder().httpClientBuilder(ApacheHttpClient.builder()).build(); + } + + /** + * This method is invoked when CloudFormation Creates the Custom Resource. + * In this example, the method creates an Amazon S3 Bucket with the provided `BucketName` + * + * @param cloudFormationCustomResourceEvent Create Event from CloudFormation + * @param context Lambda Context + * @return Response to send to CloudFormation + */ + @Override + protected Response create(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { + // Validate the CloudFormation Custom Resource event + Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); + + log.info(cloudFormationCustomResourceEvent); + String bucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"); + log.info("Bucket Name {}", bucketName); + try { + // Create the S3 bucket with the given bucketName + createBucket(bucketName); + // Return a successful response with the bucketName as the physicalResourceId + 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); + return Response.failed(bucketName); + } + } + + /** + * This method is invoked when CloudFormation Updates the Custom Resource. + * In this example, the method creates an Amazon S3 Bucket with the provided `BucketName`, if the `BucketName` differs from the previous `BucketName` (for initial creation) + * + * @param cloudFormationCustomResourceEvent Update Event from CloudFormation + * @param context Lambda Context + * @return Response to send to CloudFormation + */ + @Override + protected Response update(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { + // Validate the CloudFormation Custom Resource event + Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); + + log.info(cloudFormationCustomResourceEvent); + // 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); + + // Get the BucketName from the CloudFormation Event + String newBucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"); + + // Check if the physicalResourceId equals the new BucketName + if (!physicalResourceId.equals(newBucketName)) { + // The bucket name has changed - create a new bucket + try { + // Create a new bucket with the newBucketName + createBucket(newBucketName); + // Return a successful response with the newBucketName + return Response.success(newBucketName); + } catch (AwsServiceException | SdkClientException e) { + log.error(e); + return Response.failed(newBucketName); + } + } else { + // Bucket name has not changed, and no changes are needed. + // Return a successful response with the previous physicalResourceId + return Response.success(physicalResourceId); + } + } + + /** + * This method is invoked when CloudFormation Deletes the Custom Resource. + * NOTE: CloudFormation will DELETE a resource, if during the UPDATE a new physicalResourceId is returned. + * Refer to the <a href="https://docs.powertools.aws.dev/lambda/java/utilities/custom_resources/#understanding-the-cloudformation-custom-resource-lifecycle">Powertools Java Documentation</a> for more details. + * + * @param cloudFormationCustomResourceEvent Delete Event from CloudFormation + * @param context Lambda Context + * @return Response to send to CloudFormation + */ + @Override + protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { + // Validate the CloudFormation Custom Resource event + Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), "PhysicalResourceId cannot be null."); + + log.info(cloudFormationCustomResourceEvent); + // Get the physicalResourceId. physicalResourceId is the value provided to CloudFormation in the Create request. + String bucketName = cloudFormationCustomResourceEvent.getPhysicalResourceId(); + log.info("Bucket Name {}", bucketName); + + // Check if a bucket with bucketName exists + if (bucketExists(bucketName)) { + try { + // If it exists, delete the bucket + s3Client.deleteBucket(DeleteBucketRequest.builder().bucket(bucketName).build()); + log.info("Bucket Deleted {}", bucketName); + // Return a successful response with bucketName as the physicalResourceId + return Response.success(bucketName); + } catch (AwsServiceException | SdkClientException e) { + // Return a failed response in case of errors during the bucket deletion + log.error(e); + return Response.failed(bucketName); + } + } else { + // If the bucket does not exist, return a successful response with the bucketName as the physicalResourceId + log.info("Bucket already deleted - no action"); + return Response.success(bucketName); + } + + } + + private boolean bucketExists(String bucketName) { + try { + HeadBucketResponse headBucketResponse = s3Client.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()); + if (headBucketResponse.sdkHttpResponse().isSuccessful()) { + return true; + } + } catch (NoSuchBucketException e) { + log.info("Bucket does not exist"); + return false; + } + return false; + } + + private void createBucket(String bucketName) { + S3Waiter waiter = s3Client.waiter(); + CreateBucketRequest createBucketRequest = CreateBucketRequest.builder().bucket(bucketName).build(); + s3Client.createBucket(createBucketRequest); + WaiterResponse<HeadBucketResponse> waiterResponse = waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build()); + waiterResponse.matched().response().ifPresent(log::info); + log.info("Bucket Created {}", bucketName); + } +} \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml b/examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8e8162128 --- /dev/null +++ b/examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="Lambda"> + <PatternLayout> + <pattern>%d{dd MMM yyyy HH:mm:ss,SSS} [%p] <%X{AWSRequestId}> (%t) %c:%L: %m%n</pattern> + </PatternLayout> + </Console> + </Appenders> + <Loggers> + <Root level="info"> + <AppenderRef ref="Lambda"/> + </Root> + <Logger name="JsonLogger" level="INFO" additivity="false"/> + <Logger name="software.amazon.lambda.powertools" level="DEBUG"/> + </Loggers> +</Configuration> \ No newline at end of file From 39ba43b9fd65bcf12eae771ebbcdd5669a55a20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:07:25 +0200 Subject: [PATCH 0381/1008] chore:Prep release 1.16.1 (#1296) --- CHANGELOG.md | 8 ++++++++ README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 25 files changed, 34 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775074e82..5619f0bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.16.1] - 2023-07-19 + +* 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. + + ## [1.16.0] - 2023-06-29 diff --git a/README.md b/README.md index 0a7cd0112..a841e7df7 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.16.0</version> + <version>1.16.1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.16.0</version> + <version>1.16.1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.16.0</version> + <version>1.16.1</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index c9b8ea8ae..9ea82d99a 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 198a85894..3fd582e32 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index a5d854d6b..e956c74bc 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 0607fdd14..a5e779c2d 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index c4631fd05..2104b151f 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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>1.16.1</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 4974842dd..82a8af5ba 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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>1.16.1</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index c5b811678..ea3842de0 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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>1.16.1</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 455fd66b8..89f823a55 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -2,7 +2,7 @@ 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>1.16.1</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 5fd3206b0..da00b24d1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,7 +83,7 @@ extra_javascript: extra: powertools: - version: 1.16.0 # to update after each release (we do not want snapshot version here) + version: 1.16.1 # to update after each release (we do not want snapshot version here) repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index f0c671883..152035061 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 3a846c378..0d1a3e45b 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index cf9ad45d1..63867e8d6 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ae26364dd..d08ede7e9 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.17.0-SNAPSHOT</lambda.powertools.version> + <lambda.powertools.version>1.16.1</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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8754117db..27782d4c5 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index cb4d9b802..f8856b00d 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 767fbd3ee..a84de6b2f 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index ed2d2f815..413ac3ba1 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index b50844d99..5013ba4c0 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index f21ecb412..9481f38fa 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 618aa948c..b6ae9da90 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 5737296da..bde7b7176 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 42a229f42..51315d6a0 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 1469183ef..293c599f6 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.16.1</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From b191d72f3572b67e05d0d7a077e47a3c8e969bb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:45:18 +0200 Subject: [PATCH 0382/1008] build(deps): bump aws.sdk.version from 2.20.102 to 2.20.105 (#1297) Bumps `aws.sdk.version` from 2.20.102 to 2.20.105. Updates `software.amazon.awssdk:bom` from 2.20.102 to 2.20.105 Updates `http-client-spi` from 2.20.104 to 2.20.105 Updates `url-connection-client` from 2.20.104 to 2.20.105 Updates `s3` from 2.20.104 to 2.20.105 Updates `lambda` from 2.20.104 to 2.20.105 Updates `cloudwatch` from 2.20.104 to 2.20.105 Updates `xray` from 2.20.104 to 2.20.105 Updates `cloudformation` from 2.20.104 to 2.20.105 Updates `sts` from 2.20.104 to 2.20.105 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 3fd582e32..0022485ca 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.102</aws.sdk.version> + <aws.sdk.version>2.20.105</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index ea3842de0..7d42be4df 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.104</version> + <version>2.20.105</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 152035061..69db43a9c 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.104</aws.sdk.version> + <aws.sdk.version>2.20.105</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c185cb063a4612f704d092fb21b2493706baa193 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <mriccia@amazon.com> Date: Wed, 19 Jul 2023 15:26:55 +0200 Subject: [PATCH 0383/1008] chore: update poms to SNAPSHOT version for dev (#1299) --- examples/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 9ea82d99a..c9b8ea8ae 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 0022485ca..c46bed1de 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index e956c74bc..a5d854d6b 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index a5e779c2d..0607fdd14 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 2104b151f..c4631fd05 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 82a8af5ba..4974842dd 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 7d42be4df..d66f6dda5 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 89f823a55..455fd66b8 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -2,7 +2,7 @@ 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> + <version>1.17.0-SNAPSHOT</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/pom.xml b/pom.xml index 69db43a9c..462ad1164 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 0d1a3e45b..3a846c378 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 63867e8d6..cf9ad45d1 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index d08ede7e9..ae26364dd 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.16.1</lambda.powertools.version> + <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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 27782d4c5..8754117db 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index f8856b00d..cb4d9b802 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index a84de6b2f..767fbd3ee 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 413ac3ba1..ed2d2f815 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 5013ba4c0..b50844d99 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 9481f38fa..f21ecb412 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index b6ae9da90..618aa948c 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index bde7b7176..5737296da 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 51315d6a0..42a229f42 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 293c599f6..1469183ef 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -10,7 +10,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.16.1</version> + <version>1.17.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 0ac8127d098272360105063c3646277220dfae50 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 20 Jul 2023 11:52:30 +0200 Subject: [PATCH 0384/1008] docs: Started cleaning up example doc (#1291) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Started cleaning up example doc * More work * More docs * More docs * More docs * Clean up SQS * Did validationg * Consistency * Add CF * More cleanp * Add magic back * Update examples/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Fix naming * fix syntax --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- docs/index.md | 2 +- examples/README.md | 62 ++++++- .../README.md | 17 +- examples/powertools-examples-core/README.md | 151 ++++-------------- .../powertools-examples-idempotency/README.md | 25 +-- .../src/main/java/helloworld/App.java | 23 ++- .../powertools-examples-parameters/README.md | 37 +++-- .../template.yaml | 3 + .../README.md | 76 +++++++-- .../template.yaml | 2 +- examples/powertools-examples-sqs/README.md | 57 ++++++- .../powertools-examples-validation/README.md | 27 ++-- 12 files changed, 293 insertions(+), 189 deletions(-) diff --git a/docs/index.md b/docs/index.md index f00e0314d..d3e487174 100644 --- a/docs/index.md +++ b/docs/index.md @@ -253,7 +253,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl ``` ???+ tip "Why a different configuration?" - Lambda Powertools for Java is using [AspectJ](https://eclipse.dev/aspectj/doc/released/progguide/starting.html) internally + 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, diff --git a/examples/README.md b/examples/README.md index f7e6fc620..1869b4e8f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,62 @@ -## aws-lambda-powertools-examples +# 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. \ No newline at end of file +Each example can be copied from its subdirectory and used independently of the rest of this repository. + +## The Examples + +* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules +* [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API +* [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function +* [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 +* [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 + +## 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. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Java11 - [Install the 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) + +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-core module +$ cd powertools-examples-core + +# Build and deploy the example +$ sam build +$ sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modified IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +### 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. + +* [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 diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 6dbffcf37..4b53ff0aa 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -1,4 +1,4 @@ -# Cloudformation Custom Resource Example +# Powertools for AWS Lambda (Java) - Cloudformation Custom Resource Example 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/). @@ -7,20 +7,7 @@ This project contains an example of Lambda function using the CloudFormation mod This sample can be used either with the Serverless Application Model (SAM) or with CDK. ### Deploy with SAM CLI -To use the SAM CLI, you need the following tools. - -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java 8 - [Install Java 8](https://docs.aws.amazon.com/corretto/latest/corretto-8-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) - -To build and deploy this application for the first time, run the following in your shell: - -```bash -cd infra/sam -sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-20230717 -``` +To deploy it using the SAM CLI, check out the instructions for getting started in [the examples directory](../README.md) ### Deploy with CDK To use CDK you need the following tools. diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index ba8859027..a47d0d26c 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,138 +1,47 @@ -# CoreUtilities +# Powertools for AWS Lambda (Java) - Core Utilities Example -This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes [Powertools for AWS Lambda (Java) for operational best practices](https://github.com/aws-powertools/powertools-lambda-java), and the following files and folders. +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/). -- HelloWorldFunction/src/main - Code for the application's Lambda function. -- events - Invocation events that you can use to invoke the function. -- HelloWorldFunction/src/test - Unit tests for the application code. -- template.yaml - A template that defines the application's AWS resources. +It is made up of the following: -The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. - -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) +- [App.java](src/main/java/helloworld/App.java) - Code for the application's Lambda function. +- [events](events) - Invocation events that you can use to invoke the function. +- [AppTests.java](src/test/java/helloworld/AppTest.java) - Unit tests for the application code. +- [template.yaml](template.yaml) - A template that defines the application's AWS resources. ## Deploy the sample application -The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. - -To use the SAM CLI, you need the following tools. - -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java11 - [Install the 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) - -To build and deploy your application for the first time, run the following in your shell: - -```bash -Coreutilities$ sam build -Coreutilities$ sam deploy --guided -``` - -The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: - -* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. -* **AWS Region**: The AWS region you want to deploy your app to. -* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. -* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modified IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. -* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. - -You can find your API Gateway Endpoint URL in the output values displayed after deployment. - -## Use the SAM CLI to build and test locally - -Build your application with the `sam build` command. - -```bash -Coreutilities$ sam build -``` - -The SAM CLI installs dependencies defined in `HelloWorldFunction/pom.xml`, creates a deployment package, and saves it in the `.aws-sam/build` folder. - -Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. - -Run functions locally and invoke them with the `sam local invoke` command. - -```bash -Coreutilities$ sam local invoke HelloWorldFunction --event events/event.json -``` - -The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. - -```bash -Coreutilities$ sam local start-api -Coreutilities$ curl http://localhost:3000/ -``` - -The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. - -```yaml - Events: - HelloWorld: - Type: Api - Properties: - Path: /hello - Method: get -``` - -## Add a resource to your application -The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. +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) -## Fetch, tail, and filter Lambda function logs +## Test the application -To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. - -`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. +Once the app is deployed, you can invoke the endpoint like this: ```bash -Coreutilities$ sam logs -n HelloWorldFunction --stack-name <Name-of-your-deployed-stack> --tail + curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ ``` -You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). - -## Unit tests - -Tests are defined in the `HelloWorldFunction/src/test` folder in this project. - -```bash -Coreutilities$ cd HelloWorldFunction -HelloWorldFunction$ mvn test -``` +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: -## Cleanup +* **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 -To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: +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](src/main/java/helloworld/App.java). +You can also watch the trace information or log information using the SAM CLI: ```bash -aws cloudformation delete-stack --stack-name <Name-of-your-deployed-stack> -``` - -# Appendix - -## Powertools - -**Tracing** - -[Tracing utility](https://docs.powertools.aws.dev/lambda-java/core/tracing/) provides functionality to reduce the overhead of performing common tracing tasks. It traces the execution of this sample code including the response and exceptions as tracing metadata - You can visualize them in AWS X-Ray. - -**Logger** - -[Logging utility](https://docs.powertools.aws.dev/lambda-java/core/logging/) creates an opinionated application Logger with structured logging as the output, dynamically samples a percentage (samplingRate) of your logs in DEBUG mode for concurrent invocations, log incoming events as your function is invoked, and injects key information from Lambda context object into your Logger - You can visualize them in Amazon CloudWatch Logs. - -**Metrics** - -[Metrics utility](https://docs.powertools.aws.dev/lambda-java/core/metrics/) captures cold start metric of your Lambda invocation, and could add additional metrics to help you understand your application KPIs - You can visualize them in Amazon CloudWatch. - -## Resources - -See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. - -Check the [Powertools for AWS Lambda (Java)](https://docs.powertools.aws.dev/lambda-java/) for more information on how to use and configure such tools +# Tail the logs +sam logs --tail $MY_STACK -Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) +# Tail the traces +sam traces --tail +``` \ No newline at end of file diff --git a/examples/powertools-examples-idempotency/README.md b/examples/powertools-examples-idempotency/README.md index 278484a92..6f6022054 100644 --- a/examples/powertools-examples-idempotency/README.md +++ b/examples/powertools-examples-idempotency/README.md @@ -1,21 +1,22 @@ -# Idempotency +# Powertools for AWS Lambda (Java) - Idempotency Example This project contains an example of 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: ## Deploy the sample application -This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. +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) -To use the SAM CLI, you need the following tools. - -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java11 - [Install the 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) - -To build and deploy your application for the first time, run the following in your shell: +## Test the application ```bash -Coreutilities$ sam build -Coreutilities$ sam deploy --guided + 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" } +``` + +Check out [App.java](src/main/java/helloworld/App.java) to see how it works! diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index f26877c34..0a20aa3a4 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -43,7 +43,11 @@ public App(DynamoDbClient client) { } /** - * Try with: + * 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 + * by the idempotency library, and results are cached for the default 1h expiry time. + * + * 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> @@ -52,7 +56,7 @@ public App(DynamoDbClient client) { * <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 *** + @Idempotent // The magic is here! @Logging(logEvent = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); @@ -65,6 +69,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv 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); @@ -81,8 +86,18 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv } } - // we could also put the @Idempotent annotation here, but using it on the handler avoids executing the handler (cost reduction). - // Use it on other methods to handle multiple items (with SQS batch processing for example) + + /** + * Helper to retrieve the contents of the given URL and return them as a string. + * + * 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 + */ private String getPageContents(String address) throws IOException { URL url = new URL(address); try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { diff --git a/examples/powertools-examples-parameters/README.md b/examples/powertools-examples-parameters/README.md index 0f843d455..a65307f69 100644 --- a/examples/powertools-examples-parameters/README.md +++ b/examples/powertools-examples-parameters/README.md @@ -1,21 +1,38 @@ -# Parameters +# Powertools for AWS Lambda (Java) - Parameters Example 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. + ## Deploy the sample application -This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. +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) -To use the SAM CLI, you need the following tools. +## Test the application -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java11 - [Install the 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) +First, hit the URL of the application. You can do this with curl or your browser: -To build and deploy your application for the first time, run the following in your shell: +```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 build -sam deploy --guided +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/template.yaml b/examples/powertools-examples-parameters/template.yaml index 052cfcdc7..9d3bf8b0e 100644 --- a/examples/powertools-examples-parameters/template.yaml +++ b/examples/powertools-examples-parameters/template.yaml @@ -19,6 +19,9 @@ Resources: Handler: org.demo.parameters.ParametersFunction::handleRequest MemorySize: 512 Tracing: Active + Environment: + Variables: + LOG_LEVEL: INFO Policies: - AWSSecretsManagerGetSecretValuePolicy: SecretArn: !Ref UserPwd diff --git a/examples/powertools-examples-serialization/README.md b/examples/powertools-examples-serialization/README.md index 7f70da1f2..4e3f66eb0 100644 --- a/examples/powertools-examples-serialization/README.md +++ b/examples/powertools-examples-serialization/README.md @@ -1,21 +1,77 @@ -# Deserialization +# 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/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) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. +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 + +### 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: -To use the SAM CLI, you need the following tools. +``` +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. -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java11 - [Install the 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) +```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: -To build and deploy your application for the first time, run the following in your shell: +```bash +sam logs --tail --stack-name $MY_STACK --filter SQS +``` ```bash -sam build -sam deploy --guided + { + ... + "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/template.yaml b/examples/powertools-examples-serialization/template.yaml index 539d2d615..f330ec146 100644 --- a/examples/powertools-examples-serialization/template.yaml +++ b/examples/powertools-examples-serialization/template.yaml @@ -32,7 +32,7 @@ Resources: SQSEventDeserializationFunction: Type: AWS::Serverless::Function Properties: - CodeUri: Function + CodeUri: . Handler: org.demo.serialization.SQSEventDeserializationFunction::handleRequest Policies: - Statement: diff --git a/examples/powertools-examples-sqs/README.md b/examples/powertools-examples-sqs/README.md index 2b6da65b5..45f4a4a74 100644 --- a/examples/powertools-examples-sqs/README.md +++ b/examples/powertools-examples-sqs/README.md @@ -1,3 +1,56 @@ -## SqsBatchProcessingDemo +# Powertools for AWS Lambda (Java) - SQS Batch Processing Example -Demos setup of SQS Batch processing via Powertools +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-validation/README.md b/examples/powertools-examples-validation/README.md index 39afc48b9..3f6790b0c 100644 --- a/examples/powertools-examples-validation/README.md +++ b/examples/powertools-examples-validation/README.md @@ -1,21 +1,26 @@ -# Validation +# Powertools for AWS Lambda (Java) - Validation Example -This project contains an example of Lambda function using the validation 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/validation/). +This project contains an example of Lambda function using the validation 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/validation/). + +The handler [InboundValidation](src/main/java/org/demo/validation/InboundValidation.java) validates incoming HTTP requests +received from the API gateway against [schema.json](src/main/resources/schema.json). ## Deploy the sample application -This sample is based on Serverless Application Model (SAM) and you can use the SAM Command Line Interface (SAM CLI) to build it and deploy it to AWS. +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) -To use the SAM CLI, you need the following tools. +## Test the application -* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -* Java11 - [Install the 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) +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"}' +``` -To build and deploy your application for the first time, run the following in your shell: +If we break the schema - for instance, by removing one of the compulsory fields, +we will get an error back from our API and will see a `ValidationException` in the logs: ```bash -sam build -sam deploy --guided + sam logs --tail --stack-name $MY_STACK ``` From 577d03f38cc8895aa4c10ee61dd5924d92a03fe9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:24:05 +0200 Subject: [PATCH 0385/1008] build(deps): bump aws.sdk.version from 2.20.105 to 2.20.107 (#1300) Bumps `aws.sdk.version` from 2.20.105 to 2.20.107. Updates `software.amazon.awssdk:bom` from 2.20.105 to 2.20.107 Updates `http-client-spi` from 2.20.105 to 2.20.107 Updates `url-connection-client` from 2.20.105 to 2.20.107 Updates `s3` from 2.20.105 to 2.20.107 Updates `lambda` from 2.20.105 to 2.20.107 Updates `cloudwatch` from 2.20.105 to 2.20.107 Updates `xray` from 2.20.105 to 2.20.107 Updates `cloudformation` from 2.20.105 to 2.20.107 Updates `sts` from 2.20.105 to 2.20.107 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c46bed1de..98703c60a 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.105</aws.sdk.version> + <aws.sdk.version>2.20.107</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index d66f6dda5..0fbfd284c 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.105</version> + <version>2.20.107</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 462ad1164..859b20cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.105</aws.sdk.version> + <aws.sdk.version>2.20.107</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From ac5ce9d89c1e788694c6a01c249787d06b844491 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 13:33:37 +0200 Subject: [PATCH 0386/1008] build(deps): bump aws.sdk.version from 2.20.107 to 2.20.108 (#1304) Bumps `aws.sdk.version` from 2.20.107 to 2.20.108. Updates `software.amazon.awssdk:bom` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:http-client-spi` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:url-connection-client` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:s3` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:lambda` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:cloudwatch` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:xray` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:cloudformation` from 2.20.107 to 2.20.108 Updates `software.amazon.awssdk:sts` from 2.20.107 to 2.20.108 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 98703c60a..c7e35c8d8 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.107</aws.sdk.version> + <aws.sdk.version>2.20.108</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0fbfd284c..71c0eb131 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.107</version> + <version>2.20.108</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 859b20cf1..197e84a91 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.107</aws.sdk.version> + <aws.sdk.version>2.20.108</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 4c4235969ddb33222ab1f18af2c9120b361dca07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 13:39:08 +0200 Subject: [PATCH 0387/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1305) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.87.0 to 2.88.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.87.0...v2.88.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8754117db..5b6b50511 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -17,7 +17,7 @@ <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.87.0</cdk.version> + <cdk.version>2.88.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From 3594bbd1d4e0380fb1cd4fd143ac491b00f17181 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky <info@roamingthings.de> Date: Mon, 24 Jul 2023 11:31:37 +0200 Subject: [PATCH 0388/1008] fix: use default credentials provider for all provided SDK clients (#1303) --------- Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- .../core/internal/LambdaConstants.java | 4 ++++ .../persistence/DynamoDBPersistenceStore.java | 19 +++--------------- .../parameters/AppConfigProvider.java | 20 +++---------------- .../parameters/DynamoDbProvider.java | 20 +++---------------- .../powertools/parameters/SSMProvider.java | 20 +++---------------- .../parameters/SecretsProvider.java | 19 +++--------------- 6 files changed, 19 insertions(+), 83 deletions(-) diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java index bb5fc4666..ea6a6ff44 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java @@ -16,7 +16,11 @@ 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 AWS_SAM_LOCAL = "AWS_SAM_LOCAL"; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 6b5d0fcb2..783b029bb 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -15,11 +15,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; 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.DynamoDbClientBuilder; import software.amazon.awssdk.services.dynamodb.model.*; import software.amazon.awssdk.utils.StringUtils; import software.amazon.lambda.powertools.idempotency.Constants; @@ -34,10 +32,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; 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.core.internal.LambdaConstants.ON_DEMAND; import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; /** @@ -88,19 +84,10 @@ private DynamoDBPersistenceStore(String tableName, } else { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); if (idempotencyDisabledEnv == null || idempotencyDisabledEnv.equalsIgnoreCase("false")) { - DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder() + this.dynamoDbClient = DynamoDbClient.builder() .httpClient(UrlConnectionHttpClient.builder().build()) - .region(Region.of(System.getenv(AWS_REGION_ENV))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(ON_DEMAND)) { - ddbBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - this.dynamoDbClient = ddbBuilder.build(); + .region(Region.of(System.getenv(AWS_REGION_ENV))) + .build(); } else { // we do not want to create a DynamoDbClient if idempotency is disabled // null is ok as idempotency won't be called 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 index e0255125d..c62d7a2e5 100644 --- 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 @@ -1,23 +1,18 @@ package software.amazon.lambda.powertools.parameters; -import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; 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.AppConfigDataClientBuilder; 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.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import java.util.HashMap; import java.util.Map; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; - /** * Implements a {@link ParamProvider} on top of the AppConfig service. AppConfig provides * a mechanism to retrieve and update configuration of applications over time. @@ -144,19 +139,10 @@ public AppConfigProvider build() { // Create a AppConfigDataClient if we haven't been given one if (client == null) { - AppConfigDataClientBuilder appConfigDataClientBuilder = AppConfigDataClient.builder() + client = AppConfigDataClient.builder() .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { - appConfigDataClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - client = appConfigDataClientBuilder.build(); + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); } AppConfigProvider provider = new AppConfigProvider(cacheManager, client, environment, application); 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 index 1b77aed88..e09f23348 100644 --- 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 @@ -1,17 +1,14 @@ package software.amazon.lambda.powertools.parameters; -import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; 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.DynamoDbClientBuilder; 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.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @@ -20,8 +17,6 @@ import java.util.Map; import java.util.stream.Collectors; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; - /** * Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table * is described in the Powertools for AWS Lambda (Java) documentation. @@ -190,19 +185,10 @@ public DynamoDbProvider.Builder withTransformationManager(TransformationManager } private static DynamoDbClient createClient() { - DynamoDbClientBuilder dynamoDbClientBuilder = DynamoDbClient.builder() + return DynamoDbClient.builder() .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { - dynamoDbClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - return dynamoDbClientBuilder.build(); + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); } } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index 2eb2d4199..1fa4dbaab 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -13,17 +13,14 @@ */ package software.amazon.lambda.powertools.parameters; -import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; 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.SsmClientBuilder; 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.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; @@ -32,8 +29,6 @@ import java.util.HashMap; import java.util.Map; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; - /** * AWS System Manager Parameter Store Provider <br/><br/> * @@ -283,19 +278,10 @@ public SSMProvider.Builder withClient(SsmClient client) { } private static SsmClient createClient() { - SsmClientBuilder ssmClientBuilder = SsmClient.builder() + return SsmClient.builder() .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { - ssmClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - return ssmClientBuilder.build(); + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); } /** 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 index ea8b5a9d0..fd45da881 100644 --- 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 @@ -13,14 +13,11 @@ */ package software.amazon.lambda.powertools.parameters; -import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; 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.SecretsManagerClientBuilder; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; -import software.amazon.lambda.powertools.core.internal.LambdaConstants; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; @@ -30,7 +27,6 @@ import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE; /** * AWS Secrets Manager Parameter Provider<br/><br/> @@ -191,19 +187,10 @@ public Builder withClient(SecretsManagerClient client) { } private static SecretsManagerClient createClient() { - SecretsManagerClientBuilder secretsManagerClientBuilder = SecretsManagerClient.builder() + return SecretsManagerClient.builder() .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))); - - // AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start - // when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set - // fall back to the default provider chain if the mode is anything other than on-demand. - String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE); - if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) { - secretsManagerClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create()); - } - - return secretsManagerClientBuilder.build(); + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); } /** From 4bc526c39b58ed2b591598c1854dbe8c5342f08f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 13:45:45 +0200 Subject: [PATCH 0389/1008] build(deps): bump aws.sdk.version from 2.20.108 to 2.20.109 (#1308) Bumps `aws.sdk.version` from 2.20.108 to 2.20.109. Updates `software.amazon.awssdk:bom` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:http-client-spi` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:url-connection-client` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:s3` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:lambda` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:cloudwatch` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:xray` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:cloudformation` from 2.20.108 to 2.20.109 Updates `software.amazon.awssdk:sts` from 2.20.108 to 2.20.109 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c7e35c8d8..c93a0c845 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.108</aws.sdk.version> + <aws.sdk.version>2.20.109</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 71c0eb131..99a6d29f3 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.108</version> + <version>2.20.109</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 197e84a91..b4134d1bc 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.108</aws.sdk.version> + <aws.sdk.version>2.20.109</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 530968fc87d25d37e4b46e3313dc01a768eda6c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:55:58 +0200 Subject: [PATCH 0390/1008] build(deps): bump aws.sdk.version from 2.20.109 to 2.20.110 (#1312) Bumps `aws.sdk.version` from 2.20.109 to 2.20.110. Updates `software.amazon.awssdk:bom` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:http-client-spi` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:s3` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:lambda` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:cloudwatch` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:xray` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:cloudformation` from 2.20.109 to 2.20.110 Updates `software.amazon.awssdk:sts` from 2.20.109 to 2.20.110 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c93a0c845..45857eee2 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.109</aws.sdk.version> + <aws.sdk.version>2.20.110</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 99a6d29f3..d1b0c102b 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.109</version> + <version>2.20.110</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index b4134d1bc..cbd588c96 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.109</aws.sdk.version> + <aws.sdk.version>2.20.110</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From b1c688d56b5949d05b1118238914a0ad355fff15 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:04:22 +0200 Subject: [PATCH 0391/1008] Update README.md (#1311) --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a841e7df7..70cfab314 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,15 @@ The following companies, among others, use Powertools: * [MkDocs](https://www.mkdocs.org/) * [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) +## Connect + +* **Powertools for AWS Lambda on Discord**: `#java` - **[Invite link](https://discord.gg/B8zZKbbyET)** +* **Email**: <aws-lambda-powertools-feedback@amazon.com> + +## Security disclosures + +If you think you’ve found a potential security issue, please do not post it in the Issues. Instead, please follow the instructions [here](https://aws.amazon.com/security/vulnerability-reporting/) or [email AWS security directly](mailto:aws-security@amazon.com). + ## License This library is licensed under the Apache License, Version 2.0. See the LICENSE file. From 48533f73ab939df2f259875a26c29ee82eba224b Mon Sep 17 00:00:00 2001 From: Jeroen Reijn <j.reijn@gmail.com> Date: Wed, 26 Jul 2023 11:25:58 +0200 Subject: [PATCH 0392/1008] Make request for Logger explicit on current class (#1307) --- docs/core/logging.md | 18 +++++++++--------- .../demo/parameters/ParametersFunction.java | 2 +- .../handlers/IdempotencyFunction.java | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index bf5fb6767..09714a512 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -239,7 +239,7 @@ to customise what is logged. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -256,7 +256,7 @@ to customise what is logged. */ public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(AppLogEvent.class); @Logging(logEvent = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -315,7 +315,7 @@ You can set a Correlation ID using `correlationIdPath` attribute by passing a [J */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging(correlationIdPath = "/headers/my_request_id_header") public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -364,7 +364,7 @@ for known event sources, where either a request ID or X-Ray Trace ID are present */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging(correlationIdPath = CorrelationIdPathConstants.API_GATEWAY_REST) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -417,7 +417,7 @@ You can append your own keys to your existing logs via `appendKey`. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging(logEvent = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -449,7 +449,7 @@ You can remove any additional key from entry using `LoggingUtils.removeKeys()`. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging(logEvent = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -484,7 +484,7 @@ this means that custom keys can be persisted across invocations. If you want all */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging(clearState = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { @@ -545,7 +545,7 @@ specific fields from received event due to security. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); static { ObjectMapper objectMapper = new ObjectMapper(); @@ -575,7 +575,7 @@ via `samplingRate` attribute on annotation. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(); + Logger log = LogManager.getLogger(App.class); @Logging(samplingRate = 0.5) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java index 7f41e020e..f96352e86 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java @@ -23,7 +23,7 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; public class ParametersFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger log = LogManager.getLogger(); + private final static Logger log = LogManager.getLogger(ParametersFunction.class); SSMProvider ssmProvider = ParamManager.getSsmProvider(); SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java index c60336b81..0423bd90a 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java @@ -22,7 +22,7 @@ import java.util.stream.Collectors; public class IdempotencyFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger LOG = LogManager.getLogger(); + private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); public boolean handlerExecuted = false; From baa3592f12f81c13f8d076bef3fb947fc1f889d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:19:05 +0200 Subject: [PATCH 0393/1008] build(deps): bump aws.sdk.version from 2.20.110 to 2.20.111 (#1315) Bumps `aws.sdk.version` from 2.20.110 to 2.20.111. Updates `software.amazon.awssdk:bom` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:http-client-spi` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:url-connection-client` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:s3` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:lambda` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:cloudwatch` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:xray` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:cloudformation` from 2.20.110 to 2.20.111 Updates `software.amazon.awssdk:sts` from 2.20.110 to 2.20.111 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 45857eee2..843d8b5e8 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.110</aws.sdk.version> + <aws.sdk.version>2.20.111</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index d1b0c102b..78e4b0904 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.110</version> + <version>2.20.111</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index cbd588c96..ccc27b64c 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ <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.110</aws.sdk.version> + <aws.sdk.version>2.20.111</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 0e76f04f1946dc3cabb842fe4303ac122b67b563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 27 Jul 2023 10:31:19 +0200 Subject: [PATCH 0394/1008] chore: checkstyle formater & linter (#1316) * add checkstyle to the project * apply checkstyle and add copyright headers when missing * only check style on JDK 11 (fix jdk 8 build) * notice in the contribution guide --- CONTRIBUTING.md | 9 +- checkstyle.xml | 427 ++++++++++++++++++ examples/pom.xml | 14 + ...owertoolsExamplesCloudformationCdkApp.java | 5 +- .../src/main/java/helloworld/App.java | 15 +- .../src/main/java/helloworld/App.java | 70 +-- .../src/main/java/helloworld/AppStream.java | 21 +- .../src/test/java/helloworld/AppTest.java | 70 +-- .../powertools-examples-idempotency/pom.xml | 14 + .../src/main/java/helloworld/App.java | 37 +- .../src/test/java/helloworld/AppTest.java | 35 +- .../java/org/demo/parameters/MyObject.java | 14 + .../demo/parameters/ParametersFunction.java | 43 +- ...GatewayRequestDeserializationFunction.java | 32 +- .../java/org/demo/serialization/Product.java | 14 + .../SQSEventDeserializationFunction.java | 21 +- ...wayRequestDeserializationFunctionTest.java | 18 +- .../SQSEventDeserializationFunctionTest.java | 24 +- .../java/org/demo/sqs/SqsMessageSender.java | 63 ++- .../src/main/java/org/demo/sqs/SqsPoller.java | 37 +- .../powertools-examples-validation/pom.xml | 14 + .../demo/validation/InboundValidation.java | 20 +- .../validation/InboundValidationTest.java | 20 +- license-header | 13 + pom.xml | 53 +++ powertools-cloudformation/pom.xml | 22 + .../AbstractCustomResourceHandler.java | 19 +- .../CloudFormationResponse.java | 231 +++++----- .../CustomResourceResponseException.java | 14 + .../powertools/cloudformation/Response.java | 271 +++++------ .../AbstractCustomResourceHandlerTest.java | 209 +++++---- .../CloudFormationIntegrationTest.java | 99 ++-- .../CloudFormationResponseTest.java | 71 +-- .../cloudformation/ResponseTest.java | 47 +- .../NoPhysicalResourceIdSetHandler.java | 14 + .../PhysicalResourceIdSetHandler.java | 20 +- .../RuntimeExceptionThrownHandler.java | 14 + powertools-core/pom.xml | 23 + .../core/internal/LambdaConstants.java | 3 +- .../core/internal/LambdaHandlerProcessor.java | 14 +- .../core/internal/SystemWrapper.java | 14 + .../internal/LambdaHandlerProcessorTest.java | 40 +- .../lambda/powertools/e2e/Function.java | 27 +- .../amazon/lambda/powertools/e2e/Input.java | 14 + .../lambda/powertools/e2e/Function.java | 14 + .../amazon/lambda/powertools/e2e/Input.java | 14 + .../lambda/powertools/e2e/Function.java | 14 + .../amazon/lambda/powertools/e2e/Input.java | 21 +- .../lambda/powertools/e2e/Function.java | 14 + .../amazon/lambda/powertools/e2e/Input.java | 39 +- .../lambda/powertools/e2e/Function.java | 29 +- .../amazon/lambda/powertools/e2e/Input.java | 14 + powertools-e2e-tests/pom.xml | 18 + .../lambda/powertools/IdempotencyE2ET.java | 30 +- .../amazon/lambda/powertools/LoggingE2ET.java | 38 +- .../amazon/lambda/powertools/MetricsE2ET.java | 73 ++- .../lambda/powertools/ParametersE2ET.java | 43 +- .../amazon/lambda/powertools/TracingE2ET.java | 38 +- .../powertools/testutils/AppConfig.java | 17 +- .../powertools/testutils/Infrastructure.java | 304 +++++++------ .../powertools/testutils/JavaRuntime.java | 14 + .../testutils/lambda/InvocationResult.java | 18 +- .../testutils/lambda/LambdaInvoker.java | 27 +- .../testutils/logging/InvocationLogs.java | 18 +- .../testutils/metrics/MetricsFetcher.java | 105 +++-- .../testutils/tracing/SegmentDocument.java | 17 +- .../powertools/testutils/tracing/Trace.java | 17 +- .../testutils/tracing/TraceFetcher.java | 150 +++--- powertools-idempotency/pom.xml | 18 + .../powertools/idempotency/Constants.java | 3 +- .../powertools/idempotency/Idempotency.java | 52 ++- .../idempotency/IdempotencyConfig.java | 49 +- .../idempotency/IdempotencyKey.java | 3 +- .../powertools/idempotency/Idempotent.java | 4 +- ...IdempotencyAlreadyInProgressException.java | 3 +- .../IdempotencyConfigurationException.java | 3 +- ...IdempotencyInconsistentStateException.java | 3 +- ...IdempotencyItemAlreadyExistsException.java | 3 +- .../IdempotencyItemNotFoundException.java | 7 +- .../exceptions/IdempotencyKeyException.java | 3 +- .../IdempotencyPersistenceLayerException.java | 3 +- .../IdempotencyValidationException.java | 3 +- .../internal/IdempotencyHandler.java | 56 ++- .../internal/IdempotentAspect.java | 25 +- .../idempotency/internal/cache/LRUCache.java | 4 +- .../persistence/BasePersistenceStore.java | 63 +-- .../idempotency/persistence/DataRecord.java | 29 +- .../persistence/DynamoDBPersistenceStore.java | 84 ++-- .../persistence/PersistenceStore.java | 10 +- .../idempotency/DynamoDBConfig.java | 35 +- .../idempotency/IdempotencyTest.java | 24 +- .../handlers/IdempotencyEnabledFunction.java | 3 +- .../handlers/IdempotencyFunction.java | 29 +- .../handlers/IdempotencyInternalFunction.java | 7 +- ...dempotencyInternalFunctionInternalKey.java | 3 +- .../IdempotencyInternalFunctionInvalid.java | 5 +- .../IdempotencyInternalFunctionVoid.java | 5 +- .../handlers/IdempotencyStringFunction.java | 14 + .../IdempotencyWithErrorFunction.java | 3 +- .../internal/IdempotencyAspectTest.java | 71 ++- .../internal/cache/LRUCacheTest.java | 7 +- .../powertools/idempotency/model/Basket.java | 25 +- .../powertools/idempotency/model/Product.java | 11 +- .../persistence/BasePersistenceStoreTest.java | 71 +-- .../DynamoDBPersistenceStoreTest.java | 66 ++- powertools-logging/pom.xml | 22 + .../logging/CorrelationIdPathConstants.java | 18 +- .../lambda/powertools/logging/Logging.java | 6 +- .../powertools/logging/LoggingUtils.java | 19 +- .../internal/AbstractJacksonLayoutCopy.java | 381 ++++++++-------- .../logging/internal/DefaultLambdaFields.java | 12 +- .../logging/internal/JacksonFactoryCopy.java | 108 +++-- .../logging/internal/LambdaJsonLayout.java | 175 +++---- .../logging/internal/LambdaLoggingAspect.java | 71 +-- .../logging/internal/PowertoolsResolver.java | 19 +- .../internal/PowertoolsResolverFactory.java | 17 +- .../core/layout/LambdaJsonLayoutTest.java | 84 ++-- .../powertools/logging/LoggingUtilsTest.java | 12 +- ...LogToolApiGatewayHttpApiCorrelationId.java | 7 +- ...LogToolApiGatewayRestApiCorrelationId.java | 7 +- .../logging/handlers/PowerLogToolEnabled.java | 3 +- .../PowerLogToolEnabledForStream.java | 6 +- .../handlers/PowerLogToolSamplingEnabled.java | 3 +- .../logging/handlers/PowerToolDisabled.java | 3 +- .../handlers/PowerToolDisabledForStream.java | 4 +- .../handlers/PowerToolLogEventEnabled.java | 3 +- .../PowerToolLogEventEnabledForStream.java | 6 +- ...erToolLogEventEnabledWithCustomMapper.java | 20 +- .../PowertoolsLogAlbCorrelationId.java | 7 +- .../PowertoolsLogEnabledWithClearState.java | 5 +- ...PowertoolsLogEventBridgeCorrelationId.java | 12 +- .../internal/LambdaLoggingAspectTest.java | 125 ++--- powertools-metrics/pom.xml | 22 + .../emf/model/MetricsLoggerHelper.java | 18 +- .../lambda/powertools/metrics/Metrics.java | 17 + .../powertools/metrics/MetricsUtils.java | 65 ++- .../metrics/ValidationException.java | 14 + .../metrics/internal/LambdaMetricsAspect.java | 100 ++-- .../powertools/metrics/MetricsLoggerTest.java | 182 ++++---- ...ertoolsMetricsColdStartEnabledHandler.java | 18 +- ...MetricsEnabledDefaultDimensionHandler.java | 26 +- ...tricsEnabledDefaultNoDimensionHandler.java | 24 +- .../PowertoolsMetricsEnabledHandler.java | 20 +- ...PowertoolsMetricsEnabledStreamHandler.java | 21 +- ...sMetricsExceptionWhenNoMetricsHandler.java | 18 +- .../PowertoolsMetricsNoDimensionsHandler.java | 18 +- ...etricsNoExceptionWhenNoMetricsHandler.java | 18 +- ...rtoolsMetricsTooManyDimensionsHandler.java | 19 +- ...wertoolsMetricsWithExceptionInHandler.java | 18 +- .../internal/LambdaMetricsAspectTest.java | 362 ++++++++------- powertools-parameters/pom.xml | 19 +- .../parameters/AppConfigProvider.java | 85 ++-- .../powertools/parameters/BaseProvider.java | 78 ++-- .../parameters/DynamoDbProvider.java | 69 +-- .../lambda/powertools/parameters/Param.java | 19 +- .../powertools/parameters/ParamManager.java | 31 +- .../powertools/parameters/ParamProvider.java | 3 +- .../powertools/parameters/SSMProvider.java | 63 +-- .../parameters/SecretsProvider.java | 56 +-- .../parameters/cache/CacheManager.java | 7 +- .../parameters/cache/DataStore.java | 29 +- .../DynamoDbProviderSchemaException.java | 14 + .../exception/TransformationException.java | 6 +- .../internal/LambdaParametersAspect.java | 18 +- .../transform/Base64Transformer.java | 8 +- .../transform/BasicTransformer.java | 3 +- .../parameters/transform/JsonTransformer.java | 3 +- .../transform/TransformationManager.java | 23 +- .../parameters/transform/Transformer.java | 6 +- .../parameters/AppConfigProviderTest.java | 54 ++- .../parameters/BaseProviderTest.java | 87 ++-- .../parameters/DynamoDbProviderE2ETest.java | 29 +- .../parameters/DynamoDbProviderTest.java | 62 ++- .../ParamManagerIntegrationTest.java | 37 +- .../parameters/ParamManagerTest.java | 11 +- .../parameters/SSMProviderTest.java | 44 +- .../parameters/SecretsProviderTest.java | 27 +- .../parameters/cache/CacheManagerTest.java | 14 +- .../parameters/cache/DataStoreTest.java | 14 +- .../parameters/internal/AnotherObject.java | 18 +- .../parameters/internal/CustomProvider.java | 19 +- .../internal/LambdaParametersAspectTest.java | 39 +- .../transform/Base64TransformerTest.java | 12 +- .../transform/JsonTransformerTest.java | 26 +- .../transform/ObjectToDeserialize.java | 8 +- .../transform/TransformationManagerTest.java | 18 +- powertools-serialization/pom.xml | 18 + .../EventDeserializationException.java | 3 +- .../utilities/EventDeserializer.java | 54 ++- .../powertools/utilities/JsonConfig.java | 27 +- .../utilities/jmespath/Base64Function.java | 28 +- .../jmespath/Base64GZipFunction.java | 42 +- .../utilities/jmespath/JsonFunction.java | 4 +- .../utilities/EventDeserializerTest.java | 27 +- .../jmespath/Base64FunctionTest.java | 16 +- .../jmespath/Base64GZipFunctionTest.java | 31 +- .../utilities/jmespath/JsonFunctionTest.java | 27 +- .../powertools/utilities/model/Basket.java | 25 +- .../powertools/utilities/model/Order.java | 1 + .../powertools/utilities/model/Product.java | 11 +- powertools-sqs/pom.xml | 23 + .../sqs/SQSBatchProcessingException.java | 29 +- .../lambda/powertools/sqs/SqsBatch.java | 30 +- .../powertools/sqs/SqsLargeMessage.java | 15 +- .../powertools/sqs/SqsMessageHandler.java | 19 +- .../lambda/powertools/sqs/SqsUtils.java | 109 ++--- ...ippedMessageDueToFailedBatchException.java | 14 + .../powertools/sqs/internal/BatchContext.java | 194 ++++---- .../sqs/internal/SqsLargeMessageAspect.java | 146 +++--- .../SqsMessageBatchProcessorAspect.java | 22 +- .../powertools/sqs/SampleSqsHandler.java | 14 + .../sqs/SqsUtilsBatchProcessorTest.java | 298 ++++++------ .../sqs/SqsUtilsFifoBatchProcessorTest.java | 138 +++--- .../sqs/SqsUtilsLargeMessageTest.java | 164 ++++--- .../sqs/handlers/LambdaHandlerApiGateway.java | 16 +- .../PartialBatchFailureSuppressedHandler.java | 20 +- .../PartialBatchPartialFailureHandler.java | 20 +- .../handlers/PartialBatchSuccessHandler.java | 20 +- .../sqs/handlers/SqsMessageHandler.java | 14 + ...MessageHandlerWithNonRetryableHandler.java | 27 +- ...dlerWithNonRetryableHandlerWithDelete.java | 24 +- .../handlers/SqsNoDeleteMessageHandler.java | 14 + .../internal/SqsLargeMessageAspectTest.java | 121 +++-- .../SqsMessageBatchProcessorAspectTest.java | 214 +++++---- powertools-test-suite/pom.xml | 14 + .../testsuite/LoggingOrderTest.java | 114 +++-- .../handler/LoggingOrderMessageHandler.java | 14 + .../TracingLoggingStreamMessageHandler.java | 21 +- powertools-tracing/pom.xml | 23 + .../powertools/tracing/CaptureMode.java | 14 + .../lambda/powertools/tracing/Tracing.java | 9 +- .../powertools/tracing/TracingUtils.java | 55 +-- .../tracing/internal/LambdaTracingAspect.java | 35 +- .../tracing/internal/SystemWrapper.java | 14 + .../powertools/tracing/TracingUtilsTest.java | 160 ++++--- .../tracing/handlers/PowerToolDisabled.java | 3 +- .../handlers/PowerToolDisabledForStream.java | 8 +- .../handlers/PowerTracerToolEnabled.java | 3 +- ...lEnabledExplicitlyForResponseAndError.java | 7 +- .../PowerTracerToolEnabledForError.java | 7 +- .../PowerTracerToolEnabledForResponse.java | 7 +- ...oolEnabledForResponseWithCustomMapper.java | 48 +- .../PowerTracerToolEnabledForStream.java | 6 +- ...cerToolEnabledForStreamWithNoMetaData.java | 10 +- .../PowerTracerToolEnabledWithException.java | 3 +- .../PowerTracerToolEnabledWithNoMetaData.java | 7 +- ...erToolEnabledWithNoMetaDataDeprecated.java | 5 +- .../internal/LambdaTracingAspectTest.java | 278 ++++++------ .../nonhandler/PowerToolNonHandler.java | 14 + powertools-validation/pom.xml | 23 + .../powertools/validation/Validation.java | 8 +- .../validation/ValidationConfig.java | 7 +- .../validation/ValidationException.java | 3 +- .../validation/ValidationUtils.java | 24 +- .../validation/internal/ValidationAspect.java | 43 +- .../validation/ValidationUtilsTest.java | 92 ++-- .../handlers/GenericSchemaV7Handler.java | 3 +- .../handlers/MyCustomEventHandler.java | 3 +- .../SQSWithCustomEnvelopeHandler.java | 3 +- .../handlers/SQSWithWrongEnvelopeHandler.java | 3 +- .../ValidationInboundStringHandler.java | 3 +- .../ResponseEventsArgumentsProvider.java | 14 +- .../internal/ValidationAspectTest.java | 40 +- .../powertools/validation/model/Basket.java | 3 +- .../validation/model/MyCustomEvent.java | 3 +- .../powertools/validation/model/Product.java | 3 +- 266 files changed, 6810 insertions(+), 3722 deletions(-) create mode 100644 checkstyle.xml create mode 100644 license-header diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 08aeddcfe..46fab27cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,10 +31,11 @@ To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +3. Ensure local tests pass: `mvn clean test` +4. Ensure your code is formatted with the provided [checkstyle.xml](https://github.com/aws-powertools/powertools-lambda-java/blob/main/checkstyle.xml): `mvn clean verify` +5. Commit to your fork using clear commit messages. +6. Send us a pull request, answering any default questions in the pull request interface. +7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..34ef98ef2 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,427 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + +<!-- + Checkstyle configuration that checks the Google coding conventions from Google Java Style + that can be found at https://google.github.io/styleguide/javaguide.html + + Checkstyle is very configurable. Be sure to read the documentation at + http://checkstyle.org (or in your downloaded distribution). + + To completely disable a check, just comment it out or delete it from the file. + To suppress certain violations please review suppression filters. + + Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. + --> + +<module name="Checker"> + <module name="SuppressWarningsFilter"/> + + <property name="charset" value="UTF-8"/> + + <property name="severity" value="warning"/> + + <property name="fileExtensions" value="java"/> + <!-- Excludes all 'module-info.java' files --> + <!-- See https://checkstyle.org/filefilters/index.html --> + <module name="BeforeExecutionExclusionFileFilter"> + <property name="fileNamePattern" value="module\-info\.java$"/> + </module> + <!-- https://checkstyle.org/filters/suppressionfilter.html --> + <module name="SuppressionFilter"> + <property name="file" value="${org.checkstyle.google.suppressionfilter.config}" + default="checkstyle-suppressions.xml" /> + <property name="optional" value="true"/> + </module> + + <!-- Checks for whitespace --> + <!-- See http://checkstyle.org/checks/whitespace/index.html --> + <module name="FileTabCharacter"> + <property name="eachLine" value="true"/> + </module> + + <module name="LineLength"> + <property name="fileExtensions" value="java"/> + <property name="max" value="120"/> + <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> + </module> + + <module name="FileLength"/> + + <module name="Header"> + <property name="headerFile" value="license-header"/> + <property name="severity" value="error"/> + </module> + + <module name="TreeWalker"> + <module name="OuterTypeFilename"/> + <module name="IllegalTokenText"> + <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> + <property name="format" + value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/> + <property name="message" + value="Consider using special escape sequence instead of octal value or Unicode escaped value."/> + </module> + <module name="AvoidEscapedUnicodeCharacters"> + <property name="allowEscapesForControlCharacters" value="true"/> + <property name="allowByTailComment" value="true"/> + <property name="allowNonPrintableEscapes" value="true"/> + </module> + <module name="AvoidStarImport"> + <property name="severity" value="error"/> + </module> + <module name="OneTopLevelClass"> + <property name="severity" value="error"/> + </module> + <module name="NoLineWrap"> + <property name="severity" value="info"/> + <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/> + </module> + <module name="EmptyBlock"> + <property name="option" value="TEXT"/> + <property name="tokens" + value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/> + </module> + <module name="EqualsAvoidNull"> + <property name="severity" value="error"/> + </module> + <module name="EqualsHashCode"> + <property name="severity" value="error"/> + </module> + <module name="NeedBraces"> + <property name="tokens" + value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/> + </module> + <module name="LeftCurly"> + <property name="tokens" + value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF, + INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT, + LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, + LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF, + OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/> + </module> + <module name="RightCurly"> + <property name="id" value="RightCurlySame"/> + <property name="tokens" + value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, + LITERAL_DO"/> + </module> + <module name="RightCurly"> + <property name="id" value="RightCurlyAlone"/> + <property name="option" value="alone"/> + <property name="tokens" + value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, + INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF, LITERAL_SWITCH"/> + </module> + <module name="SuppressionXpathSingleFilter"> + <!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 --> + <property name="id" value="RightCurlyAlone"/> + <property name="query" value="//RCURLY[parent::SLIST[count(./*)=1] + or preceding-sibling::*[last()][self::LCURLY]]"/> + </module> + <module name="WhitespaceAfter"> + <property name="tokens" + value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN, + LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS, + LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA, + LITERAL_YIELD, LITERAL_CASE"/> + </module> + <module name="WhitespaceAround"> + <property name="allowEmptyConstructors" value="true"/> + <property name="allowEmptyLambdas" value="true"/> + <property name="allowEmptyMethods" value="true"/> + <property name="allowEmptyTypes" value="true"/> + <property name="allowEmptyLoops" value="true"/> + <property name="ignoreEnhancedForColon" value="false"/> + <property name="tokens" + value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, + BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND, + LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, + LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, + LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, + NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, + SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/> + <message key="ws.notFollowed" + value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks + may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> + <message key="ws.notPreceded" + value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/> + </module> + <module name="OneStatementPerLine"/> + <module name="MultipleVariableDeclarations"/> + <module name="ArrayTypeStyle"/> + <module name="MissingSwitchDefault"/> + <module name="FallThrough"> + <property name="severity" value="error"/> + </module> + <module name="UpperEll"/> + <module name="ModifierOrder"/> + <module name="EmptyLineSeparator"> + <property name="tokens" + value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF"/> + <property name="allowNoEmptyLineBetweenFields" value="true"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapDot"/> + <property name="tokens" value="DOT"/> + <property name="option" value="nl"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapComma"/> + <property name="tokens" value="COMMA"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 --> + <property name="id" value="SeparatorWrapEllipsis"/> + <property name="tokens" value="ELLIPSIS"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 --> + <property name="id" value="SeparatorWrapArrayDeclarator"/> + <property name="tokens" value="ARRAY_DECLARATOR"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapMethodRef"/> + <property name="tokens" value="METHOD_REF"/> + <property name="option" value="nl"/> + </module> + <module name="PackageName"> + <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> + <message key="name.invalidPattern" + value="Package name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="TypeName"> + <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + ANNOTATION_DEF, RECORD_DEF"/> + <message key="name.invalidPattern" + value="Type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="MemberName"> + <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/> + <message key="name.invalidPattern" + value="Member name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="ParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="LambdaParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="CatchParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Catch parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="LocalVariableName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Local variable name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="PatternVariableName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Pattern variable name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="ClassTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Class type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="RecordComponentName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Record component name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="RecordTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Record type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="MethodTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Method type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="InterfaceTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Interface type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="NoFinalizer"/> + <module name="GenericWhitespace"> + <message key="ws.followed" + value="GenericWhitespace ''{0}'' is followed by whitespace."/> + <message key="ws.preceded" + value="GenericWhitespace ''{0}'' is preceded with whitespace."/> + <message key="ws.illegalFollow" + value="GenericWhitespace ''{0}'' should followed by whitespace."/> + <message key="ws.notPreceded" + value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> + </module> + <module name="Indentation"> + <property name="basicOffset" value="4"/> + <property name="braceAdjustment" value="4"/> + <property name="caseIndent" value="4"/> + <property name="throwsIndent" value="8"/> + <property name="lineWrappingIndentation" value="8"/> + <property name="arrayInitIndent" value="4"/> + </module> + <module name="AbbreviationAsWordInName"> + <property name="ignoreFinal" value="false"/> + <property name="allowedAbbreviationLength" value="4"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF, + PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF, + RECORD_COMPONENT_DEF"/> + </module> + <module name="NoWhitespaceBeforeCaseDefaultColon"/> + <module name="OverloadMethodsDeclarationOrder"/> + <module name="VariableDeclarationUsageDistance"/> + <module name="CustomImportOrder"> + <property name="sortImportsInGroupAlphabetically" value="true"/> + <property name="separateLineBetweenGroups" value="true"/> + <property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/> + <property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/> + </module> + <module name="MethodParamPad"> + <property name="tokens" + value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF, + SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/> + </module> + <module name="NoWhitespaceBefore"> + <property name="tokens" + value="COMMA, SEMI, POST_INC, POST_DEC, DOT, + LABELED_STAT, METHOD_REF"/> + <property name="allowLineBreaks" value="true"/> + </module> + <module name="ParenPad"> + <property name="tokens" + value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF, + EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, + LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL, + METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA, + RECORD_DEF"/> + </module> + <module name="OperatorWrap"> + <property name="option" value="NL"/> + <property name="tokens" + value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, + LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF, + TYPE_EXTENSION_AND "/> + </module> + <module name="AnnotationLocation"> + <property name="id" value="AnnotationLocationMostCases"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, + RECORD_DEF, COMPACT_CTOR_DEF"/> + </module> + <module name="AnnotationLocation"> + <property name="id" value="AnnotationLocationVariables"/> + <property name="tokens" value="VARIABLE_DEF"/> + <property name="allowSamelineMultipleAnnotations" value="true"/> + </module> + <module name="NonEmptyAtclauseDescription"> + <property name="severity" value="info"/> + </module> + <module name="InvalidJavadocPosition"> + <property name="severity" value="info"/> + </module> + <module name="JavadocTagContinuationIndentation"> + <property name="severity" value="ignore"/> + </module> + <module name="SummaryJavadoc"> + <property name="severity" value="ignore"/> + <property name="forbiddenSummaryFragments" + value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> + </module> + <module name="JavadocParagraph"> + <property name="severity" value="ignore"/> + </module> + <module name="RequireEmptyLineBeforeBlockTagGroup"/> + <module name="AtclauseOrder"> + <property name="tagOrder" value="@param, @return, @throws, @deprecated"/> + <property name="target" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/> + </module> + <module name="JavadocMethod"> + <property name="severity" value="info"/> + <property name="accessModifiers" value="public"/> + <property name="allowMissingParamTags" value="true"/> + <property name="allowMissingReturnTag" value="true"/> + <property name="allowedAnnotations" value="Override, Test"/> + <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/> + </module> + <module name="MissingJavadocMethod"> + <property name="severity" value="info"/> + <property name="scope" value="public"/> + <property name="minLineCount" value="1"/> + <property name="allowedAnnotations" value="Override, Test"/> + <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, + COMPACT_CTOR_DEF"/> + </module> + <module name="MissingJavadocType"> + <property name="severity" value="info"/> + <property name="scope" value="protected"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + RECORD_DEF, ANNOTATION_DEF"/> + <property name="excludeScope" value="nothing"/> + </module> + <module name="MethodName"> + <property name="format" value="^[a-z][a-z0-9]\w*$"/> + <message key="name.invalidPattern" + value="Method name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="SingleLineJavadoc"> + <property name="severity" value="ignore"/> + </module> + <module name="EmptyCatchBlock"> + <property name="exceptionVariableName" value="expected"/> + </module> + <module name="UnusedImports"> + <property name="severity" value="error"/> + </module> + <module name="UnusedLocalVariable"> + <property name="severity" value="error"/> + </module> + <module name="CommentsIndentation"> + <property name="severity" value="info"/> + <property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/> + </module> + <module name="CyclomaticComplexity" /> + <module name="DefaultComesLast"> + <property name="severity" value="error"/> + </module> + <!-- https://checkstyle.org/filters/suppressionxpathfilter.html --> + <module name="SuppressionXpathFilter"> + <property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}" + default="checkstyle-xpath-suppressions.xml" /> + <property name="optional" value="true"/> + </module> + <module name="SuppressWarningsHolder" /> + <module name="SuppressionCommentFilter"> + <property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)" /> + <property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)" /> + <property name="checkFormat" value="$1" /> + </module> + <module name="SuppressWithNearbyCommentFilter"> + <property name="commentFormat" value="CHECKSTYLE.SUPPRESS\: ([\w\|]+)"/> + <!-- $1 refers to the first match group in the regex defined in commentFormat --> + <property name="checkFormat" value="$1"/> + <!-- The check is suppressed in the next line of code after the comment --> + <property name="influenceFormat" value="1"/> + </module> + </module> +</module> diff --git a/examples/pom.xml b/examples/pom.xml index c9b8ea8ae..72f1dc03b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -1,4 +1,18 @@ <?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"> diff --git a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java index 84060171b..f4a4d06d7 100644 --- a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java +++ b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java @@ -7,8 +7,9 @@ public class PowertoolsExamplesCloudformationCdkApp { public static void main(final String[] args) { App app = new App(); - new PowertoolsExamplesCloudformationCdkStack(app, "PowertoolsExamplesCloudformationCdkStack", StackProps.builder() - .build()); + new PowertoolsExamplesCloudformationCdkStack(app, "PowertoolsExamplesCloudformationCdkStack", + StackProps.builder() + .build()); app.synth(); } 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 c7744cd5a..ca3cb0ab7 100644 --- a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java @@ -41,7 +41,8 @@ public App() { protected Response create(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { // Validate the CloudFormation Custom Resource event Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); - Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), + "BucketName cannot be null."); log.info(cloudFormationCustomResourceEvent); String bucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"); @@ -70,7 +71,8 @@ protected Response create(CloudFormationCustomResourceEvent cloudFormationCustom protected Response update(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { // Validate the CloudFormation Custom Resource event Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); - Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), + "BucketName cannot be null."); log.info(cloudFormationCustomResourceEvent); // 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) @@ -112,7 +114,8 @@ protected Response update(CloudFormationCustomResourceEvent cloudFormationCustom protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { // Validate the CloudFormation Custom Resource event Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); - Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), "PhysicalResourceId cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), + "PhysicalResourceId cannot be null."); log.info(cloudFormationCustomResourceEvent); // Get the physicalResourceId. physicalResourceId is the value provided to CloudFormation in the Create request. @@ -142,7 +145,8 @@ protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustom private boolean bucketExists(String bucketName) { try { - HeadBucketResponse headBucketResponse = s3Client.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()); + HeadBucketResponse headBucketResponse = + s3Client.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()); if (headBucketResponse.sdkHttpResponse().isSuccessful()) { return true; } @@ -157,7 +161,8 @@ private void createBucket(String bucketName) { S3Waiter waiter = s3Client.waiter(); CreateBucketRequest createBucketRequest = CreateBucketRequest.builder().bucket(bucketName).build(); s3Client.createBucket(createBucketRequest); - WaiterResponse<HeadBucketResponse> waiterResponse = waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build()); + WaiterResponse<HeadBucketResponse> waiterResponse = + waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build()); waiterResponse.matched().response().ifPresent(log::info); log.info("Bucket Created {}", bucketName); } diff --git a/examples/powertools-examples-core/src/main/java/helloworld/App.java b/examples/powertools-examples-core/src/main/java/helloworld/App.java index b45440114..94360cf59 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core/src/main/java/helloworld/App.java @@ -1,12 +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 helloworld; -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 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 static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -14,21 +25,23 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Entity; +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.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; -import software.amazon.lambda.powertools.tracing.TracingUtils; import software.amazon.lambda.powertools.tracing.Tracing; - -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 static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; +import software.amazon.lambda.powertools.tracing.TracingUtils; /** * Handler for requests to Lambda function. @@ -47,10 +60,11 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); LoggingUtils.appendKey("test", "willBeLogged"); @@ -62,11 +76,12 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv 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); - }); + TracingUtils.withSubsegment("loggingResponse", subsegment -> + { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); threadOption1(); @@ -91,10 +106,11 @@ private void threadOption1() throws InterruptedException { private void threadOption2() throws InterruptedException { Entity traceEntity = AWSXRay.getTraceEntity(); - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); + Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> + { + String var = "somethingToProcess"; + log.info("inside threaded logging inline {}", var); + })); anotherThread.start(); anotherThread.join(); } diff --git a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java index aed048eef..401ef8c48 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java @@ -1,13 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.util.Map; - -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; diff --git a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java index b584ee944..70dad8d71 100644 --- a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java +++ b/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java @@ -1,43 +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 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; -import static org.junit.Assert.*; - public class AppTest { - @Before - public void setup() { - if(null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.beginSegment("test"); + @Before + public void setup() { + if (null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.beginSegment("test"); + } } - } - @After - public void tearDown() { - if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { - AWSXRay.endSubsegment(); + @After + public void tearDown() { + if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { + AWSXRay.endSubsegment(); + } + + if (null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.endSegment(); + } } - 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\"")); } - } - - @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 index 0607fdd14..f24b2ffc4 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -1,3 +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. + ~ + --> + <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> diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index 0a20aa3a4..ac2c7ef1b 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -1,9 +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 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.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; @@ -14,14 +35,6 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.utilities.JsonConfig; -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; - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { private final static Logger log = LogManager.getLogger(App.class); @@ -32,7 +45,8 @@ 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` + .withEventKeyJMESPath( + "powertools_json(body).address") // will retrieve the address field in the body which is a string transformed to json with `powertools_json` .build()) .withPersistenceStore( DynamoDBPersistenceStore.builder() @@ -45,7 +59,7 @@ public App(DynamoDbClient client) { /** * 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 * by the idempotency library, and results are cached for the default 1h expiry time. - * + * <p> * You can test the endpoint like this: * * <pre> @@ -89,13 +103,12 @@ 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 * @return The contents of the given URL - * * @throws IOException */ private String getPageContents(String address) throws IOException { diff --git a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java index 7a5304e36..7f097906a 100644 --- a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java +++ b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java @@ -1,3 +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. + * + */ + package helloworld; import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; @@ -5,6 +19,9 @@ 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; @@ -16,23 +33,24 @@ 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.*; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; +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; - private static DynamoDbClient client; @BeforeAll public static void setupDynamoLocal() { int port = getFreePort(); try { - DynamoDBProxyServer dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[]{ + DynamoDBProxyServer dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[] { "-inMemory", "-port", Integer.toString(port) @@ -79,7 +97,8 @@ void setUp() { @Test public void testApp() { - APIGatewayProxyResponseEvent response = app.handleRequest(EventLoader.loadApiGatewayRestEvent("event.json"), context); + 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-parameters/src/main/java/org/demo/parameters/MyObject.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java index 2cf145284..d406ae3df 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java @@ -1,3 +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. + * + */ + package org.demo.parameters; public class MyObject { diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java index f96352e86..5b691cfd9 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java @@ -1,15 +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 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 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 java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -17,10 +29,11 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; - -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 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; public class ParametersFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { private final static Logger log = LogManager.getLogger(ParametersFunction.class); @@ -34,8 +47,10 @@ public class ParametersFunction implements RequestHandler<APIGatewayProxyRequest 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); + 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) { @@ -72,9 +87,9 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent in } } - private String getPageContents(String address) throws IOException{ + private String getPageContents(String address) throws IOException { URL url = new URL(address); - try(BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { + 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-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java index 8c33baed9..e70b37959 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java @@ -1,19 +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 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 org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.HashMap; import java.util.Map; - -import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -public class APIGatewayRequestDeserializationFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +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>() {{ @@ -28,9 +42,9 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent ev LOGGER.info("product={}\n", product); return new APIGatewayProxyResponseEvent() - .withHeaders(HEADERS) - .withStatusCode(200) - .withBody("Received request for productId: " + product.getId()); + .withHeaders(HEADERS) + .withStatusCode(200) + .withBody("Received request for productId: " + product.getId()); } } diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java index fb94a99f8..25bae34f6 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java @@ -1,3 +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. + * + */ + package org.demo.serialization; public class Product { diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java index 129fe0243..36dbed074 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java @@ -1,15 +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 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 java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.List; - -import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; - public class SQSEventDeserializationFunction implements RequestHandler<SQSEvent, String> { 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 index 5d5da7ecc..ec8cdbd33 100644 --- 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 @@ -1,5 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; @@ -8,8 +24,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; - class APIGatewayRequestDeserializationFunctionTest { @Mock 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 index 6979a6868..b46af3052 100644 --- 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 @@ -1,17 +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 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; -import java.util.List; -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.assertEquals; - class SQSEventDeserializationFunctionTest { @Mock @@ -29,7 +41,7 @@ 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>(){{ + event.setRecords(new ArrayList<SQSEvent.SQSMessage>() {{ add(message1); add(message2); }}); 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 index b90c50654..45856d198 100644 --- 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 @@ -1,11 +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 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; @@ -17,15 +38,6 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; -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 static java.util.stream.Collectors.toList; - public class SqsMessageSender implements RequestHandler<ScheduledEvent, String> { private static final Logger log = LogManager.getLogger(SqsMessageSender.class); @@ -50,22 +62,23 @@ public String handleRequest(final ScheduledEvent input, final Context context) { // 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()); + .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) 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 index bf2b7bdfe..9ad5c7868 100644 --- 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 @@ -1,10 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 java.util.Random; +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; @@ -13,18 +29,12 @@ import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; import software.amazon.lambda.powertools.sqs.SqsUtils; -import java.security.SecureRandom; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; /** * Handler for requests to Lambda function. */ public class SqsPoller implements RequestHandler<SQSEvent, String> { - Logger log = LogManager.getLogger(SqsPoller.class); - Random random = new SecureRandom(); - static { // https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/lambda-optimize-starttime.html SqsUtils.overrideSqsClient(SqsClient.builder() @@ -32,6 +42,9 @@ public class SqsPoller implements RequestHandler<SQSEvent, String> { .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) { @@ -45,14 +58,16 @@ public String process(SQSMessage message) { int nextInt = random.nextInt(100); - if(nextInt <= 10) { + 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()); + throw new IllegalArgumentException( + "Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); } - if(nextInt > 90) { + 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()); + throw new RuntimeException( + "Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); } return "Success"; diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 455fd66b8..1c7e33de0 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -1,3 +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. + ~ + --> + <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> diff --git a/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java b/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java index 89ec538c9..d3b8e51e4 100644 --- a/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java +++ b/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java @@ -1,11 +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 org.demo.validation; 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; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -13,6 +25,7 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import software.amazon.lambda.powertools.validation.Validation; /** * Request handler for Lambda function which demonstrates validation of request message. @@ -20,7 +33,8 @@ public class InboundValidation implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @Validation(inboundSchema = "classpath:/schema.json") - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, + Context context) { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); 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 index af47d3d87..d5e6de313 100644 --- 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 @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; @@ -9,9 +26,6 @@ import org.mockito.MockitoAnnotations; import software.amazon.lambda.powertools.validation.ValidationException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - public class InboundValidationTest { @Mock diff --git a/license-header b/license-header new file mode 100644 index 000000000..5669f143f --- /dev/null +++ b/license-header @@ -0,0 +1,13 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ diff --git a/pom.xml b/pom.xml index ccc27b64c..7744913fd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -525,6 +539,45 @@ </plugins> </build> </profile> + <profile> + <id>jdk11</id> + <activation> + <jdk>11</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.9.1</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> </profiles> </project> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 3a846c378..a122e7ac8 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -100,4 +114,12 @@ </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-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java index 7d3a43069..7f5b6bb24 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java @@ -1,16 +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.cloudformation; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import java.io.IOException; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import java.io.IOException; -import java.util.Objects; - /** * Handler base class providing core functionality for sending responses to custom CloudFormation resources after * receiving some event. Depending on the type of event, this class either invokes the crete, update, or delete method 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 39a86293b..2f020aa25 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.cloudformation; import com.amazonaws.services.lambda.runtime.Context; @@ -6,6 +20,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.Header; @@ -16,14 +37,6 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.utils.StringInputStream; -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - /** * Client for sending responses to AWS CloudFormation custom resources by way of a response URL, which is an Amazon S3 * pre-signed URL. @@ -35,102 +48,6 @@ class CloudFormationResponse { private static final Logger LOG = LoggerFactory.getLogger(CloudFormationResponse.class); - - /** - * Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload - * except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of - * the value of "Data" can be handled by separate ObjectMappers, if need be. The former properties are dictated by - * the custom resource but the latter is dictated by the implementor of the custom resource handler. - */ - @SuppressWarnings("unused") - static class ResponseBody { - static final ObjectMapper MAPPER = new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); - private static final String DATA_PROPERTY_NAME = "Data"; - - private final String status; - private final String reason; - private final String physicalResourceId; - private final String stackId; - private final String requestId; - private final String logicalResourceId; - private final boolean noEcho; - - ResponseBody(CloudFormationCustomResourceEvent event, - Response.Status responseStatus, - String physicalResourceId, - boolean noEcho, - String reason) { - Objects.requireNonNull(event, "CloudFormationCustomResourceEvent cannot be null"); - - this.physicalResourceId = physicalResourceId; - this.reason = reason; - this.status = responseStatus == null ? Response.Status.SUCCESS.name() : responseStatus.name(); - this.stackId = event.getStackId(); - this.requestId = event.getRequestId(); - this.logicalResourceId = event.getLogicalResourceId(); - this.noEcho = noEcho; - } - - public String getStatus() { - return status; - } - - public String getReason() { - return reason; - } - - public String getPhysicalResourceId() { - return physicalResourceId; - } - - public String getStackId() { - return stackId; - } - - public String getRequestId() { - return requestId; - } - - public String getLogicalResourceId() { - return logicalResourceId; - } - - public boolean isNoEcho() { - return noEcho; - } - - /** - * Returns this ResponseBody as an ObjectNode with the provided JsonNode as the value of its "Data" property. - * - * @param dataNode the value of the "Data" property for the returned node; may be null - * @return an ObjectNode representation of this ResponseBody and the provided dataNode - */ - ObjectNode toObjectNode(JsonNode dataNode) { - ObjectNode node = MAPPER.valueToTree(this); - if (dataNode == null) { - node.putNull(DATA_PROPERTY_NAME); - } else { - node.set(DATA_PROPERTY_NAME, dataNode); - } - return node; - } - - @Override - public String toString() { - final StringBuffer sb = new StringBuffer("ResponseBody{"); - sb.append("status='").append(status).append('\''); - sb.append(", reason='").append(reason).append('\''); - sb.append(", physicalResourceId='").append(physicalResourceId).append('\''); - sb.append(", stackId='").append(stackId).append('\''); - sb.append(", requestId='").append(requestId).append('\''); - sb.append(", logicalResourceId='").append(logicalResourceId).append('\''); - sb.append(", noEcho=").append(noEcho); - sb.append('}'); - return sb.toString(); - } - } - private final SdkHttpClient client; /** @@ -212,7 +129,7 @@ protected Map<String, List<String>> headers(int contentLength) { /** * Returns the response body as an input stream, for supplying with the HTTP request to the custom resource. - * + * <p> * If PhysicalResourceId is null at this point it will be replaced with the Lambda LogStreamName. * * @throws CustomResourceResponseException if unable to generate the response stream @@ -223,7 +140,8 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, try { String reason = "See the details in CloudWatch Log Stream: " + context.getLogStreamName(); if (resp == null) { - String physicalResourceId = event.getPhysicalResourceId() != null? event.getPhysicalResourceId() : context.getLogStreamName(); + String physicalResourceId = event.getPhysicalResourceId() != null ? event.getPhysicalResourceId() : + context.getLogStreamName(); ResponseBody body = new ResponseBody(event, Response.Status.SUCCESS, physicalResourceId, false, reason); LOG.debug("ResponseBody: {}", body); @@ -232,9 +150,11 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, } else { String physicalResourceId = resp.getPhysicalResourceId() != null ? resp.getPhysicalResourceId() : - event.getPhysicalResourceId() != null? event.getPhysicalResourceId() : context.getLogStreamName(); + event.getPhysicalResourceId() != null ? event.getPhysicalResourceId() : + context.getLogStreamName(); - ResponseBody body = new ResponseBody(event, resp.getStatus(), physicalResourceId, resp.isNoEcho(), reason); + ResponseBody body = + new ResponseBody(event, resp.getStatus(), physicalResourceId, resp.isNoEcho(), reason); LOG.debug("ResponseBody: {}", body); ObjectNode node = body.toObjectNode(resp.getJsonNode()); return new StringInputStream(node.toString()); @@ -244,4 +164,99 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, throw new CustomResourceResponseException("Unable to generate response body.", e); } } + + /** + * Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload + * except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of + * the value of "Data" can be handled by separate ObjectMappers, if need be. The former properties are dictated by + * the custom resource but the latter is dictated by the implementor of the custom resource handler. + */ + @SuppressWarnings("unused") + static class ResponseBody { + static final ObjectMapper MAPPER = new ObjectMapper() + .setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); + private static final String DATA_PROPERTY_NAME = "Data"; + + private final String status; + private final String reason; + private final String physicalResourceId; + private final String stackId; + private final String requestId; + private final String logicalResourceId; + private final boolean noEcho; + + ResponseBody(CloudFormationCustomResourceEvent event, + Response.Status responseStatus, + String physicalResourceId, + boolean noEcho, + String reason) { + Objects.requireNonNull(event, "CloudFormationCustomResourceEvent cannot be null"); + + this.physicalResourceId = physicalResourceId; + this.reason = reason; + this.status = responseStatus == null ? Response.Status.SUCCESS.name() : responseStatus.name(); + this.stackId = event.getStackId(); + this.requestId = event.getRequestId(); + this.logicalResourceId = event.getLogicalResourceId(); + this.noEcho = noEcho; + } + + public String getStatus() { + return status; + } + + public String getReason() { + return reason; + } + + public String getPhysicalResourceId() { + return physicalResourceId; + } + + public String getStackId() { + return stackId; + } + + public String getRequestId() { + return requestId; + } + + public String getLogicalResourceId() { + return logicalResourceId; + } + + public boolean isNoEcho() { + return noEcho; + } + + /** + * Returns this ResponseBody as an ObjectNode with the provided JsonNode as the value of its "Data" property. + * + * @param dataNode the value of the "Data" property for the returned node; may be null + * @return an ObjectNode representation of this ResponseBody and the provided dataNode + */ + ObjectNode toObjectNode(JsonNode dataNode) { + ObjectNode node = MAPPER.valueToTree(this); + if (dataNode == null) { + node.putNull(DATA_PROPERTY_NAME); + } else { + node.set(DATA_PROPERTY_NAME, dataNode); + } + return node; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("ResponseBody{"); + sb.append("status='").append(status).append('\''); + sb.append(", reason='").append(reason).append('\''); + sb.append(", physicalResourceId='").append(physicalResourceId).append('\''); + sb.append(", stackId='").append(stackId).append('\''); + sb.append(", requestId='").append(requestId).append('\''); + sb.append(", logicalResourceId='").append(logicalResourceId).append('\''); + sb.append(", noEcho=").append(noEcho); + sb.append('}'); + return sb.toString(); + } + } } diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java index ead912392..904ae9c3f 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.cloudformation; /** 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 f388f6384..fe18000d4 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 @@ -1,8 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; - import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @@ -13,119 +26,16 @@ */ public class Response { - /** - * Indicates whether a response is a success or failure. - */ - public enum Status { - SUCCESS, FAILED - } - - /** - * For building Response instances. - */ - public static class Builder { - private Object value; - private ObjectMapper objectMapper; - private Status status; - private String physicalResourceId; - private boolean noEcho; - - private Builder() { - } - - /** - * Configures the value of this Response, typically a Map of name/value pairs. - * - * @param value if null, the Response will be empty - * @return a reference to this builder - */ - public Builder value(Object value) { - this.value = value; - return this; - } - - /** - * Configures a custom ObjectMapper for serializing the value object. Creates a copy of the mapper provided; - * future mutations of the ObjectMapper made using the provided reference will not affect Response - * serialization. - * - * @param objectMapper if null, a default mapper will be used - * @return a reference to this builder - */ - public Builder objectMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper == null ? null : objectMapper.copy(); - return this; - } - - /** - * Configures the status of this response. - * - * @param status if null, SUCCESS will be assumed - * @return a reference to this builder - */ - public Builder status(Status status) { - this.status = status; - return this; - } - - /** - * A unique identifier for the custom resource being responded to. By default, the identifier is the name of the - * Amazon CloudWatch Logs log stream associated with the Lambda function. - * - * @param physicalResourceId if null, the default resource ID will be used - * @return a reference to this builder - */ - public Builder physicalResourceId(String physicalResourceId) { - this.physicalResourceId = physicalResourceId; - return this; - } - - /** - * Indicates whether to mask the output of the custom resource when it's retrieved by using the Fn::GetAtt - * function. If set to true, values will be masked with asterisks (*****), except for information stored in the - * these locations: - * <ul> - * <li>The Metadata template section. CloudFormation does not transform, modify, or redact any information - * included in the Metadata section. See - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html</li> - * <li>The Outputs template section. See - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html</li> - * <li>The Metadata attribute of a resource definition. See - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-metadata.html</li> - * </ul> - * <p> - * We strongly recommend not using these mechanisms to include sensitive information, such as passwords or - * secrets. - * <p> - * For more information about using noEcho to mask sensitive information, see - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html#creds - * <p> - * By default, this value is false. - * - * @param noEcho when true, masks certain output - * @return a reference to this builder - */ - public Builder noEcho(boolean noEcho) { - this.noEcho = noEcho; - return this; - } + private final JsonNode jsonNode; + private final Status status; + private final String physicalResourceId; + private final boolean noEcho; - /** - * Builds a Response object for the value. - * - * @return a Response object wrapping the initially provided value. - */ - public Response build() { - JsonNode node; - if (value == null) { - node = null; - } else { - ObjectMapper mapper = objectMapper != null ? objectMapper : CloudFormationResponse.ResponseBody.MAPPER; - node = mapper.valueToTree(value); - } - Status responseStatus = this.status != null ? this.status : Status.SUCCESS; - return new Response(node, responseStatus, physicalResourceId, noEcho); - } + private Response(JsonNode jsonNode, Status status, String physicalResourceId, boolean noEcho) { + this.jsonNode = jsonNode; + this.status = status; + this.physicalResourceId = physicalResourceId; + this.noEcho = noEcho; } /** @@ -140,14 +50,14 @@ public static Builder 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. * - * @deprecated this method is not safe. Provide a physicalResourceId. * @return a failed Response with no value. + * @deprecated this method is not safe. Provide a physicalResourceId. */ @Deprecated public static Response failed() { @@ -173,14 +83,14 @@ public static Response failed(String physicalResourceId) { /** * 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. * - * @deprecated this method is not safe. Provide a physicalResourceId. * @return a success Response with no physicalResourceId value. + * @deprecated this method is not safe. Provide a physicalResourceId. */ @Deprecated public static Response success() { @@ -203,18 +113,6 @@ public static Response success(String physicalResourceId) { return new Response(null, Status.SUCCESS, physicalResourceId, false); } - private final JsonNode jsonNode; - private final Status status; - private final String physicalResourceId; - private final boolean noEcho; - - private Response(JsonNode jsonNode, Status status, String physicalResourceId, boolean noEcho) { - this.jsonNode = jsonNode; - this.status = status; - this.physicalResourceId = physicalResourceId; - this.noEcho = noEcho; - } - /** * Returns a JsonNode representation of the Response. * @@ -267,4 +165,119 @@ public String toString() { .map(entry -> entry.getKey() + " = " + entry.getValue()) .collect(Collectors.joining(",", "[", "]")); } + + /** + * Indicates whether a response is a success or failure. + */ + public enum Status { + SUCCESS, FAILED + } + + /** + * For building Response instances. + */ + public static class Builder { + private Object value; + private ObjectMapper objectMapper; + private Status status; + private String physicalResourceId; + private boolean noEcho; + + private Builder() { + } + + /** + * Configures the value of this Response, typically a Map of name/value pairs. + * + * @param value if null, the Response will be empty + * @return a reference to this builder + */ + public Builder value(Object value) { + this.value = value; + return this; + } + + /** + * Configures a custom ObjectMapper for serializing the value object. Creates a copy of the mapper provided; + * future mutations of the ObjectMapper made using the provided reference will not affect Response + * serialization. + * + * @param objectMapper if null, a default mapper will be used + * @return a reference to this builder + */ + public Builder objectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper == null ? null : objectMapper.copy(); + return this; + } + + /** + * Configures the status of this response. + * + * @param status if null, SUCCESS will be assumed + * @return a reference to this builder + */ + public Builder status(Status status) { + this.status = status; + return this; + } + + /** + * A unique identifier for the custom resource being responded to. By default, the identifier is the name of the + * Amazon CloudWatch Logs log stream associated with the Lambda function. + * + * @param physicalResourceId if null, the default resource ID will be used + * @return a reference to this builder + */ + public Builder physicalResourceId(String physicalResourceId) { + this.physicalResourceId = physicalResourceId; + return this; + } + + /** + * Indicates whether to mask the output of the custom resource when it's retrieved by using the Fn::GetAtt + * function. If set to true, values will be masked with asterisks (*****), except for information stored in the + * these locations: + * <ul> + * <li>The Metadata template section. CloudFormation does not transform, modify, or redact any information + * included in the Metadata section. See + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html</li> + * <li>The Outputs template section. See + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html</li> + * <li>The Metadata attribute of a resource definition. See + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-metadata.html</li> + * </ul> + * <p> + * We strongly recommend not using these mechanisms to include sensitive information, such as passwords or + * secrets. + * <p> + * For more information about using noEcho to mask sensitive information, see + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html#creds + * <p> + * By default, this value is false. + * + * @param noEcho when true, masks certain output + * @return a reference to this builder + */ + public Builder noEcho(boolean noEcho) { + this.noEcho = noEcho; + return this; + } + + /** + * Builds a Response object for the value. + * + * @return a Response object wrapping the initially provided value. + */ + public Response build() { + JsonNode node; + if (value == null) { + node = null; + } else { + ObjectMapper mapper = objectMapper != null ? objectMapper : CloudFormationResponse.ResponseBody.MAPPER; + node = mapper.valueToTree(value); + } + Status responseStatus = this.status != null ? this.status : Status.SUCCESS; + return new Response(node, responseStatus, physicalResourceId, noEcho); + } + } } 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 d68b434d6..1e399ef6f 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 @@ -1,14 +1,18 @@ -package software.amazon.lambda.powertools.cloudformation; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import software.amazon.awssdk.http.SdkHttpClient; -import software.amazon.lambda.powertools.cloudformation.Response.Status; - -import java.io.IOException; +package software.amazon.lambda.powertools.cloudformation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -21,95 +25,16 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class AbstractCustomResourceHandlerTest { - - /** - * 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; - } - } +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 software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.lambda.powertools.cloudformation.Response.Status; - /** - * 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; - } - } +public class AbstractCustomResourceHandlerTest { /** * Builds a valid Event with the provide request type. @@ -288,4 +213,92 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte verify(handler, times(1)) .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 06463308c..ce45d3afc 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 @@ -1,5 +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.cloudformation; +import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.put; +import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +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; @@ -7,6 +30,7 @@ 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; @@ -14,20 +38,47 @@ import software.amazon.lambda.powertools.cloudformation.handlers.PhysicalResourceIdSetHandler; import software.amazon.lambda.powertools.cloudformation.handlers.RuntimeExceptionThrownHandler; -import java.util.UUID; - -import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static org.assertj.core.api.Assertions.assertThat; - @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(); + } + + private static CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder baseEvent(int httpPort) { + CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = + CloudFormationCustomResourceEvent.builder() + .withResponseUrl("http://localhost:" + httpPort + "/") + .withStackId("123") + .withRequestId("234") + .withLogicalResourceId("345"); + + return builder; + } + @ParameterizedTest @ValueSource(strings = {"Update", "Delete"}) - void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(String requestType, + WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); @@ -48,7 +99,8 @@ void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(St @ParameterizedTest @ValueSource(strings = {"Update", "Delete"}) - void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDeleting(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDeleting(String requestType, + WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); @@ -68,7 +120,7 @@ void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDele } @Test - void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMockRuntimeInfo wmRuntimeInfo) { + void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); @@ -85,7 +137,8 @@ void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMo @ParameterizedTest @ValueSource(strings = {"Update", "Delete"}) - void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAPhysicalResourceId(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAPhysicalResourceId( + String requestType, WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); @@ -160,34 +213,6 @@ void physicalResourceIdReturnedFromFailedToCloudformation(String requestType, Wi ); } - 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(); - } - - private static CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder baseEvent(int httpPort) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = CloudFormationCustomResourceEvent.builder() - .withResponseUrl("http://localhost:" + httpPort + "/") - .withStackId("123") - .withRequestId("234") - .withLogicalResourceId("345"); - - return builder; - } - private static class FakeContext implements Context { @Override public String getAwsRequestId() { 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 64c313695..2e7fbcc0c 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 @@ -1,8 +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.cloudformation; +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.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 software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.ExecutableHttpRequest; @@ -13,18 +38,6 @@ import software.amazon.awssdk.utils.StringInputStream; import software.amazon.lambda.powertools.cloudformation.CloudFormationResponse.ResponseBody; -import java.io.IOException; -import java.io.InputStream; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -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.mock; -import static org.mockito.Mockito.when; - public class CloudFormationResponseTest { /** @@ -44,16 +57,17 @@ static CloudFormationResponse testableCloudFormationResponse() { SdkHttpClient client = mock(SdkHttpClient.class); ExecutableHttpRequest executableRequest = mock(ExecutableHttpRequest.class); - when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> { - HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); - assertThat(request.contentStreamProvider()).isPresent(); + when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> + { + HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); + assertThat(request.contentStreamProvider()).isPresent(); - InputStream inputStream = request.contentStreamProvider().get().newStream(); - HttpExecuteResponse response = mock(HttpExecuteResponse.class); - when(response.responseBody()).thenReturn(Optional.of(AbortableInputStream.create(inputStream))); - when(executableRequest.call()).thenReturn(response); - return executableRequest; - }); + InputStream inputStream = request.contentStreamProvider().get().newStream(); + HttpExecuteResponse response = mock(HttpExecuteResponse.class); + when(response.responseBody()).thenReturn(Optional.of(AbortableInputStream.create(inputStream))); + when(executableRequest.call()).thenReturn(response); + return executableRequest; + }); return new CloudFormationResponse(client); } @@ -113,7 +127,8 @@ void customPhysicalResponseId() { String customPhysicalResourceId = "Custom-Physical-Resource-ID"; ResponseBody body = new ResponseBody( - event, Response.Status.SUCCESS, customPhysicalResourceId, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + event, Response.Status.SUCCESS, customPhysicalResourceId, false, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getPhysicalResourceId()).isEqualTo(customPhysicalResourceId); } @@ -122,7 +137,8 @@ void responseBodyWithNullDataNode() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); Context context = mock(Context.class); - ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); String actualJson = responseBody.toObjectNode(null).toString(); String expectedJson = "{" + @@ -146,7 +162,8 @@ void responseBodyWithNonNullDataNode() { dataNode.put("foo", "bar"); dataNode.put("baz", 10); - ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); String actualJson = responseBody.toObjectNode(dataNode).toString(); String expectedJson = "{" + @@ -178,7 +195,8 @@ void customStatus() { Context context = mock(Context.class); ResponseBody body = new ResponseBody( - event, Response.Status.FAILED, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + event, Response.Status.FAILED, null, false, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getStatus()).isEqualTo("FAILED"); } @@ -191,7 +209,8 @@ void reasonIncludesLogStreamName() { when(context.getLogStreamName()).thenReturn(logStreamName); ResponseBody body = new ResponseBody( - event, Response.Status.SUCCESS, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + event, Response.Status.SUCCESS, null, false, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getReason()).contains(logStreamName); } 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 e97a1a5ba..37fe73d0f 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 @@ -1,29 +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.cloudformation; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import org.junit.jupiter.api.Test; - import java.util.HashMap; import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; public class ResponseTest { - static class DummyBean { - private final Object propertyWithLongName; - - DummyBean(Object propertyWithLongName) { - this.propertyWithLongName = propertyWithLongName; - } - - @SuppressWarnings("unused") - public Object getPropertyWithLongName() { - return propertyWithLongName; - } - } - @Test void defaultValues() { Response response = Response.builder().build(); @@ -173,4 +173,17 @@ void failedFactoryMethod() { assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(Response.Status.FAILED); } + + static class DummyBean { + private final Object propertyWithLongName; + + DummyBean(Object propertyWithLongName) { + this.propertyWithLongName = propertyWithLongName; + } + + @SuppressWarnings("unused") + public Object getPropertyWithLongName() { + return propertyWithLongName; + } + } } 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 68d057b54..2bbda309f 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.cloudformation.handlers; import com.amazonaws.services.lambda.runtime.Context; 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 51f520a3d..c6bd56b76 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.cloudformation.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -17,16 +31,16 @@ public PhysicalResourceIdSetHandler(String physicalResourceId, boolean callsSucc @Override protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + return callsSucceed ? Response.success(physicalResourceId) : Response.failed(physicalResourceId); } @Override protected Response update(CloudFormationCustomResourceEvent event, Context context) { - return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + return callsSucceed ? Response.success(physicalResourceId) : Response.failed(physicalResourceId); } @Override protected Response delete(CloudFormationCustomResourceEvent event, Context context) { - return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + return callsSucceed ? Response.success(physicalResourceId) : Response.failed(physicalResourceId); } } 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 ee5be77b8..d5a11e895 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.cloudformation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index cf9ad45d1..1adb64af8 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -89,4 +103,13 @@ </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-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java index ea6a6ff44..d0f94260b 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.core.internal; public class LambdaConstants { diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index c7f8b119f..e9e220e41 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.core.internal; +import static java.util.Optional.empty; +import static java.util.Optional.of; +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 org.aspectj.lang.ProceedingJoinPoint; - import java.io.InputStream; import java.io.OutputStream; import java.util.Optional; - -import static java.util.Optional.empty; -import static java.util.Optional.of; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; +import org.aspectj.lang.ProceedingJoinPoint; public final class LambdaHandlerProcessor { diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java index aef64378f..30f72232f 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.core.internal; public class SystemWrapper { diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java index 94ad97506..dc8f49580 100644 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java +++ b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java @@ -1,24 +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.core.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.Test; import org.mockito.MockedStatic; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Optional; - -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; - class LambdaHandlerProcessorTest { private Signature signature = mock(Signature.class); @@ -42,7 +55,7 @@ void isHandlerMethod_shouldRecognizeRequestStreamHandler() { @Test void isHandlerMethod_shouldReturnFalse() { - ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, new Object[]{}); + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, new Object[] {}); boolean isHandlerMethod = LambdaHandlerProcessor.isHandlerMethod(pjpMock); @@ -210,7 +223,8 @@ void isSamLocal() { void serviceName() { try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) { String expectedServiceName = "MyService"; - mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)).thenReturn(expectedServiceName); + mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)) + .thenReturn(expectedServiceName); String actualServiceName = LambdaHandlerProcessor.serviceName(); 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 cc6eec4fa..8c2b5fc58 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 @@ -1,7 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.TimeZone; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -10,12 +29,6 @@ import software.amazon.lambda.powertools.idempotency.Idempotent; import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; -import java.time.Duration; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.TimeZone; - public class Function implements RequestHandler<Input, String> { @@ -42,7 +55,7 @@ public Function(DynamoDbClient client) { @Idempotent public String handleRequest(Input input, Context context) { - DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + 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/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index c5c2a121e..e0e4c27c9 100644 --- a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; public class Input { diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 5a9a87109..62ebabc6e 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 83afbbd5a..cc449922e 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; import java.util.Map; 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 e643de9d5..d9cf575c3 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; 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 5ff8a7125..18c4eb747 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; import java.util.Map; @@ -7,6 +21,9 @@ public class Input { private Map<String, String> dimensions; + public Input() { + } + public Map<String, Double> getMetrics() { return metrics; } @@ -15,10 +32,6 @@ public void setMetrics(Map<String, Double> metrics) { this.metrics = metrics; } - public Input() { - } - - public Map<String, String> getDimensions() { return dimensions; } 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 a2d6a1ba6..3a83a1b05 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 7ea22143f..b481d25e1 100644 --- a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,34 +1,47 @@ -package software.amazon.lambda.powertools.e2e; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import java.util.Map; +package software.amazon.lambda.powertools.e2e; public class Input { private String app; private String environment; private String key; - public void setApp(String app) { - this.app = app; - } - - public void setEnvironment(String environment) { - this.environment = environment; - } - - public void setKey(String key) { - this.key = key; - } public String getApp() { return app; } + public void setApp(String app) { + this.app = app; + } + public String getEnvironment() { return environment; } + public void setEnvironment(String environment) { + this.environment = environment; + } + public String getKey() { return key; } + public void setKey(String key) { + this.key = key; + } + } 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 f7b2c5e5d..462b7c71d 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; @@ -16,13 +30,14 @@ public String handleRequest(Input input, Context context) { } String message = buildMessage(input.getMessage(), context.getFunctionName()); - TracingUtils.withSubsegment("internal_stuff", subsegment -> { - try { - Thread.sleep(100); // simulate stuff - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - }); + TracingUtils.withSubsegment("internal_stuff", subsegment -> + { + try { + Thread.sleep(100); // simulate stuff + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); return message; } 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 29cf618ba..92078d0b3 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.e2e; public class Input { diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 5b6b50511..3e7f0f460 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -133,6 +147,10 @@ <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> 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 6923c3caa..fed823299 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 @@ -1,5 +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; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +import java.time.Year; +import java.util.Collections; +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; @@ -8,13 +28,6 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -import java.time.Year; -import java.util.Collections; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; - public class IdempotencyE2ET { private static Infrastructure infrastructure; private static String functionName; @@ -34,8 +47,9 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test 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 15c5eb935..e4b8dccd2 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 @@ -1,8 +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; +import static org.assertj.core.api.Assertions.assertThat; +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.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; @@ -10,15 +32,6 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; -import static software.amazon.lambda.powertools.testutils.logging.InvocationLogs.Level.INFO; - public class LoggingE2ET { private static final ObjectMapper objectMapper = new ObjectMapper(); @@ -33,7 +46,7 @@ public static void setup() { .testName(LoggingE2ET.class.getSimpleName()) .pathToFunction("logging") .environmentVariables( - Stream.of(new String[][]{ + Stream.of(new String[][] { {"POWERTOOLS_LOG_LEVEL", "INFO"}, {"POWERTOOLS_SERVICE_NAME", LoggingE2ET.class.getSimpleName()} }) @@ -44,15 +57,16 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { // GIVEN String orderId = UUID.randomUUID().toString(); - String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId +"\"}}"; + String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId + "\"}}"; // WHEN InvocationResult invocationResult1 = invokeFunction(functionName, event); 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 4b8d7ea5a..32965b3be 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 @@ -1,12 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 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.metrics.MetricsFetcher; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.util.Collections; import java.util.List; @@ -14,13 +23,17 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +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.metrics.MetricsFetcher; public class MetricsE2ET { - private static final String namespace = "MetricsE2ENamespace_"+UUID.randomUUID(); - private static final String service = "MetricsE2EService_"+UUID.randomUUID(); + private static final String namespace = "MetricsE2ENamespace_" + UUID.randomUUID(); + private static final String service = "MetricsE2EService_" + UUID.randomUUID(); private static Infrastructure infrastructure; private static String functionName; @@ -31,7 +44,7 @@ public static void setup() { .testName(MetricsE2ET.class.getSimpleName()) .pathToFunction("metrics") .environmentVariables( - Stream.of(new String[][]{ + Stream.of(new String[][] { {"POWERTOOLS_METRICS_NAMESPACE", namespace}, {"POWERTOOLS_SERVICE_NAME", service} }) @@ -42,32 +55,44 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test - public void test_recordMetrics() { + public void test_recordMetrics() { // GIVEN - String event1 = "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; + String event1 = + "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; // WHEN InvocationResult invocationResult = invokeFunction(functionName, event1); // 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")); + 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")); + 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)); + 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)); + productMetrics = + metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, + "products", Collections.singletonMap("Service", service)); assertThat(productMetrics.get(0)).isEqualTo(4); } } 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 dbebe721f..678321a99 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 @@ -1,9 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 org.junit.jupiter.api.*; -import software.amazon.lambda.powertools.testutils.AppConfig; -import software.amazon.lambda.powertools.testutils.Infrastructure; -import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.util.HashMap; import java.util.Map; @@ -11,23 +23,29 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +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 { + private final AppConfig appConfig; private Infrastructure infrastructure; private String functionName; - private final AppConfig appConfig; public ParametersE2ET() { String appName = UUID.randomUUID().toString(); - Map<String,String> params = new HashMap<>(); + Map<String, String> params = new HashMap<>(); params.put("key1", "value1"); params.put("key2", "value2"); appConfig = new AppConfig(appName, "e2etest", params); } + @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) public void setup() { @@ -36,7 +54,7 @@ public void setup() { .pathToFunction("parameters") .appConfig(appConfig) .environmentVariables( - Stream.of(new String[][]{ + Stream.of(new String[][] { {"POWERTOOLS_LOG_LEVEL", "INFO"}, {"POWERTOOLS_SERVICE_NAME", ParametersE2ET.class.getSimpleName()} }) @@ -47,13 +65,14 @@ public void setup() { @AfterAll public void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test public void test_getAppConfigValue() { - for (Map.Entry<String, String >configKey: appConfig.getConfigurationValues().entrySet()) { + for (Map.Entry<String, String> configKey : appConfig.getConfigurationValues().entrySet()) { // Arrange String event1 = "{" + 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 1f002fe60..cd4a21df4 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 @@ -1,5 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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; @@ -10,16 +31,9 @@ import software.amazon.lambda.powertools.testutils.tracing.Trace; import software.amazon.lambda.powertools.testutils.tracing.TraceFetcher; -import java.util.Collections; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; - public class TracingE2ET { - private static final String service = "TracingE2EService_" + UUID.randomUUID(); // "TracingE2EService_e479fb27-422b-4107-9f8c-086c62e1cd12"; + private static final String service = "TracingE2EService_" + UUID.randomUUID(); + // "TracingE2EService_e479fb27-422b-4107-9f8c-086c62e1cd12"; private static Infrastructure infrastructure; private static String functionName; @@ -38,8 +52,9 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test @@ -77,7 +92,8 @@ public void test_tracing() { sub = handleRequest.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 = handleRequest.getSubsegments().stream() + .filter(subSegment -> subSegment.getName().equals("## buildMessage")).findFirst().orElse(null); assertThat(buildMessage).isNotNull(); assertThat(buildMessage.getAnnotations()).hasSize(1); assertThat(buildMessage.getAnnotations().get("message")).isEqualTo(message); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java index c87f4ac48..4229af040 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java @@ -1,12 +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; -import java.util.HashMap; import java.util.Map; /** * Defines configuration used to setup an AppConfig * deployment when the infrastructure is rolled out. - * + * <p> * All fields are non-nullable. */ public class AppConfig { 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 59035af7c..62bb018f4 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 @@ -1,23 +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.testutils; +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.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 software.amazon.awscdk.App; +import software.amazon.awscdk.BundlingOptions; +import software.amazon.awscdk.BundlingOutput; +import software.amazon.awscdk.DockerVolume; +import software.amazon.awscdk.Duration; +import software.amazon.awscdk.RemovalPolicy; import software.amazon.awscdk.Stack; -import software.amazon.awscdk.*; import software.amazon.awscdk.cxapi.CloudAssembly; -import software.amazon.awscdk.services.appconfig.*; +import software.amazon.awscdk.services.appconfig.CfnApplication; +import software.amazon.awscdk.services.appconfig.CfnConfigurationProfile; +import software.amazon.awscdk.services.appconfig.CfnDeployment; +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.Attribute; import software.amazon.awscdk.services.dynamodb.AttributeType; import software.amazon.awscdk.services.dynamodb.BillingMode; import software.amazon.awscdk.services.dynamodb.Table; -import software.amazon.awscdk.services.groundstation.CfnConfig; import software.amazon.awscdk.services.iam.PolicyStatement; -import software.amazon.awscdk.services.iam.ServicePrincipal; import software.amazon.awscdk.services.lambda.Code; import software.amazon.awscdk.services.lambda.Function; -import software.amazon.awscdk.services.lambda.Permission; import software.amazon.awscdk.services.lambda.Tracing; import software.amazon.awscdk.services.logs.LogGroup; import software.amazon.awscdk.services.logs.RetentionDays; @@ -27,7 +60,12 @@ import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.cloudformation.CloudFormationClient; -import software.amazon.awssdk.services.cloudformation.model.*; +import software.amazon.awssdk.services.cloudformation.model.Capability; +import software.amazon.awssdk.services.cloudformation.model.CreateStackRequest; +import software.amazon.awssdk.services.cloudformation.model.DeleteStackRequest; +import software.amazon.awssdk.services.cloudformation.model.DescribeStacksRequest; +import software.amazon.awssdk.services.cloudformation.model.DescribeStacksResponse; +import software.amazon.awssdk.services.cloudformation.model.OnFailure; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; @@ -36,14 +74,6 @@ import software.amazon.awssdk.utils.StringUtils; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.*; -import java.util.stream.Collectors; - -import static java.util.Collections.singletonList; - /** * This class is in charge of bootstrapping the infrastructure for the tests. * <br/> @@ -71,12 +101,10 @@ public class Infrastructure { private final String account; private final String idempotencyTable; private final AppConfig appConfig; - - + private final SdkHttpClient httpClient; private String functionName; private Object cfnTemplate; private String cfnAssetDirectory; - private final SdkHttpClient httpClient; private Infrastructure(Builder builder) { this.stackName = builder.stackName; @@ -110,8 +138,13 @@ private Infrastructure(Builder builder) { .build(); } + public static Builder builder() { + return new Builder(); + } + /** * Use the CloudFormation SDK to create the stack + * * @return the name of the function deployed part of the stack */ public String deploy() { @@ -124,9 +157,11 @@ public 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()) { - LOG.info("Stack " + waiterResponse.matched().response().get().stacks().get(0).stackName() + " successfully deployed"); + LOG.info("Stack " + waiterResponse.matched().response().get().stacks().get(0).stackName() + + " successfully deployed"); } else { throw new RuntimeException("Failed to create stack"); } @@ -141,93 +176,9 @@ public void destroy() { cfn.deleteStack(DeleteStackRequest.builder().stackName(stackName).build()); } - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - public long timeoutInSeconds = 30; - public String pathToFunction; - public String testName; - public AppConfig appConfig; - private String stackName; - private boolean tracing = false; - private JavaRuntime runtime; - private Map<String, String> environmentVariables = new HashMap<>(); - private String idemPotencyTable; - - private Builder() { - getJavaRuntime(); - } - - /** - * 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 - if (javaVersion == null) { - throw new IllegalArgumentException("JAVA_VERSION is not set"); - } - if (javaVersion.startsWith("8")) { - runtime = JavaRuntime.JAVA8AL2; - } else if (javaVersion.startsWith("11")) { - runtime = JavaRuntime.JAVA11; - } else if (javaVersion.startsWith("17")) { - runtime = JavaRuntime.JAVA17; - } else { - throw new IllegalArgumentException("Unsupported Java version " + javaVersion); - } - LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime()); - } - - public Infrastructure build() { - Objects.requireNonNull(testName, "testName must not be null"); - - String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12); - stackName = testName + "-" + uuid; - - Objects.requireNonNull(pathToFunction, "pathToFunction must not be null"); - return new Infrastructure(this); - } - - public Builder testName(String testName) { - this.testName = testName; - return this; - } - - public Builder pathToFunction(String pathToFunction) { - this.pathToFunction = pathToFunction; - return this; - } - - public Builder tracing(boolean tracing) { - this.tracing = tracing; - return this; - } - - public Builder idempotencyTable(String tableName) { - this.idemPotencyTable = tableName; - return this; - } - - public Builder appConfig(AppConfig app) { - this.appConfig = app; - return this; - } - - public Builder environmentVariables(Map<String, String> environmentVariables) { - this.environmentVariables = environmentVariables; - return this; - } - - public Builder timeoutInSeconds(long timeoutInSeconds) { - this.timeoutInSeconds = timeoutInSeconds; - return this; - } - } - /** * Build the CDK Stack containing the required resources (Lambda function, LogGroup, DDB Table) + * * @return the CDK stack */ private Stack createStackWithLambda() { @@ -259,7 +210,8 @@ private Stack createStackWithLambda() { functionName = stackName + "-function"; - LOG.debug("Building Lambda function with command "+ packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); + LOG.debug("Building Lambda function with command " + + packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); Function function = Function.Builder .create(stack, functionName) .code(Code.fromAsset("handlers/", AssetOptions.builder() @@ -297,10 +249,10 @@ private Stack createStackWithLambda() { } if (appConfig != null) { - CfnApplication app = CfnApplication.Builder - .create(stack, "AppConfigApp") - .name(appConfig.getApplication()) - .build(); + CfnApplication app = CfnApplication.Builder + .create(stack, "AppConfigApp") + .name(appConfig.getApplication()) + .build(); CfnEnvironment environment = CfnEnvironment.Builder .create(stack, "AppConfigEnvironment") @@ -327,7 +279,7 @@ private Stack createStackWithLambda() { ); CfnDeployment previousDeployment = null; - for (Map.Entry<String,String> entry : appConfig.getConfigurationValues().entrySet()) { + for (Map.Entry<String, String> entry : appConfig.getConfigurationValues().entrySet()) { CfnConfigurationProfile configProfile = CfnConfigurationProfile.Builder .create(stack, "AppConfigProfileFor" + entry.getKey()) .applicationId(app.getRef()) @@ -376,43 +328,133 @@ private void synthesize() { */ private void uploadAssets() { Map<String, Asset> assets = findAssets(); - assets.forEach((objectKey, asset) -> { - if (!asset.assetPath.endsWith(".jar")) { - return; - } - 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"); - return; - } - LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); - s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), Paths.get(cfnAssetDirectory, asset.assetPath)); - }); + assets.forEach((objectKey, asset) -> + { + if (!asset.assetPath.endsWith(".jar")) { + return; + } + 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"); + return; + } + LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); + s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), + Paths.get(cfnAssetDirectory, asset.assetPath)); + }); } /** * Reading the cdk assets.json file to retrieve the list of assets to push to S3 + * * @return a map of assets */ private Map<String, Asset> findAssets() { Map<String, Asset> assets = new HashMap<>(); try { - JsonNode jsonNode = JsonConfig.get().getObjectMapper().readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); + JsonNode jsonNode = JsonConfig.get().getObjectMapper() + .readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); JsonNode files = jsonNode.get("files"); - 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(); - Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account).replace("${AWS::Region}", region.toString())); - assets.put(objectKey, asset); - }); + 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(); + Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account) + .replace("${AWS::Region}", region.toString())); + assets.put(objectKey, asset); + }); } catch (IOException e) { throw new RuntimeException(e); } return assets; } + public static class Builder { + public long timeoutInSeconds = 30; + public String pathToFunction; + public String testName; + public AppConfig appConfig; + private String stackName; + private boolean tracing = false; + private JavaRuntime runtime; + private Map<String, String> environmentVariables = new HashMap<>(); + private String idemPotencyTable; + + private Builder() { + getJavaRuntime(); + } + + /** + * 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 + if (javaVersion == null) { + throw new IllegalArgumentException("JAVA_VERSION is not set"); + } + if (javaVersion.startsWith("8")) { + runtime = JavaRuntime.JAVA8AL2; + } else if (javaVersion.startsWith("11")) { + runtime = JavaRuntime.JAVA11; + } else if (javaVersion.startsWith("17")) { + runtime = JavaRuntime.JAVA17; + } else { + throw new IllegalArgumentException("Unsupported Java version " + javaVersion); + } + LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime()); + } + + public Infrastructure build() { + Objects.requireNonNull(testName, "testName must not be null"); + + String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12); + stackName = testName + "-" + uuid; + + Objects.requireNonNull(pathToFunction, "pathToFunction must not be null"); + return new Infrastructure(this); + } + + public Builder testName(String testName) { + this.testName = testName; + return this; + } + + public Builder pathToFunction(String pathToFunction) { + this.pathToFunction = pathToFunction; + return this; + } + + public Builder tracing(boolean tracing) { + this.tracing = tracing; + return this; + } + + public Builder idempotencyTable(String tableName) { + this.idemPotencyTable = tableName; + return this; + } + + public Builder appConfig(AppConfig app) { + this.appConfig = app; + return this; + } + + public Builder environmentVariables(Map<String, String> environmentVariables) { + this.environmentVariables = environmentVariables; + return this; + } + + public Builder timeoutInSeconds(long timeoutInSeconds) { + this.timeoutInSeconds = timeoutInSeconds; + return this; + } + } + private static class Asset { private final String assetPath; private final String assetPackaging; 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 dce97538f..c50fcab84 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.testutils; import software.amazon.awscdk.services.lambda.Runtime; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java index 168fec71b..b91840b8e 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java @@ -1,10 +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.testutils.lambda; +import java.time.Instant; import software.amazon.awssdk.services.lambda.model.InvokeResponse; import software.amazon.lambda.powertools.testutils.logging.InvocationLogs; -import java.time.Instant; - public class InvocationResult { private final InvocationLogs logs; @@ -21,6 +34,7 @@ public InvocationResult(InvokeResponse response, Instant start, Instant end) { this.start = start; this.end = end; } + public InvocationLogs getLogs() { return logs; } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java index ecde1042e..cf45076bf 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java @@ -1,5 +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.testutils.lambda; +import static java.time.temporal.ChronoUnit.MINUTES; + +import java.time.Clock; +import java.time.Instant; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -9,18 +27,13 @@ import software.amazon.awssdk.services.lambda.model.InvokeResponse; import software.amazon.awssdk.services.lambda.model.LogType; -import java.time.Clock; -import java.time.Instant; - -import static java.time.temporal.ChronoUnit.MINUTES; - public class LambdaInvoker { 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 final LambdaClient lambda = LambdaClient.builder() .httpClient(httpClient) - .region(region) - .build(); + .region(region) + .build(); public static InvocationResult invokeFunction(String functionName, String input) { SdkBytes payload = SdkBytes.fromUtf8String(input); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java index 1ae1cfad7..cd63d308a 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.testutils.logging; import java.nio.charset.StandardCharsets; @@ -35,6 +49,7 @@ public String[] getAllLogs() { /** * Return only logs from function, exclude START, END, and REPORT and other elements generated by Lambda service + * * @return only logs generated by the function */ public String[] getFunctionLogs() { @@ -44,7 +59,8 @@ public String[] getFunctionLogs() { public String[] getFunctionLogs(Level level) { String[] filtered = getFunctionLogs(); - return Arrays.stream(filtered).filter(log -> log.contains("\"level\":\""+level.getLevel()+"\"")).toArray(String[]::new); + return Arrays.stream(filtered).filter(log -> log.contains("\"level\":\"" + level.getLevel() + "\"")) + .toArray(String[]::new); } public enum Level { 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 eb3cd63a4..349a9acc1 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 @@ -1,25 +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.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.Map; +import java.util.concurrent.Callable; 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; import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; -import software.amazon.awssdk.services.cloudwatch.model.*; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; - -import static java.time.Duration.ofSeconds; +import software.amazon.awssdk.services.cloudwatch.model.Dimension; +import software.amazon.awssdk.services.cloudwatch.model.GetMetricDataRequest; +import software.amazon.awssdk.services.cloudwatch.model.GetMetricDataResponse; +import software.amazon.awssdk.services.cloudwatch.model.Metric; +import software.amazon.awssdk.services.cloudwatch.model.MetricDataQuery; +import software.amazon.awssdk.services.cloudwatch.model.MetricStat; +import software.amazon.awssdk.services.cloudwatch.model.StandardUnit; /** * Class in charge of retrieving the actual metrics of a Lambda execution on CloudWatch @@ -37,6 +56,7 @@ public class MetricsFetcher { /** * 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 * @param end * @param period @@ -45,37 +65,41 @@ public class MetricsFetcher { * @param dimensions * @return */ - public List<Double> fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, Map<String, String> dimensions) { + public List<Double> fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, + Map<String, String> dimensions) { List<Dimension> dimensionsList = new ArrayList<>(); - if (dimensions != null) + if (dimensions != null) { dimensions.forEach((key, value) -> dimensionsList.add(Dimension.builder().name(key).value(value).build())); + } - Callable<List<Double>> callable = () -> { - 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()) - .metricStat(MetricStat.builder() - .unit(StandardUnit.COUNT) - .metric(Metric.builder() - .namespace(namespace) - .metricName(metricName) - .dimensions(dimensionsList) - .build()) - .period(period) - .stat("Sum") - .build()) - .returnData(true) - .build()) - .build()); - List<Double> values = metricData.metricDataResults().get(0).values(); - if (values == null || values.isEmpty()) { - throw new Exception("No data found for metric " + metricName); - } - return values; - }; + Callable<List<Double>> callable = () -> + { + 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()) + .metricStat(MetricStat.builder() + .unit(StandardUnit.COUNT) + .metric(Metric.builder() + .namespace(namespace) + .metricName(metricName) + .dimensions(dimensionsList) + .build()) + .period(period) + .stat("Sum") + .build()) + .returnData(true) + .build()) + .build()); + List<Double> values = metricData.metricDataResults().get(0).values(); + if (values == null || values.isEmpty()) { + throw new Exception("No data found for metric " + metricName); + } + return values; + }; RetryConfig retryConfig = new RetryConfigBuilder() .withMaxNumberOfTries(10) @@ -85,9 +109,10 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String .build(); CallExecutor<List<Double>> callExecutor = new CallExecutorBuilder<List<Double>>() .config(retryConfig) - .afterFailedTryListener(s -> { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); - }) + .afterFailedTryListener(s -> + { + LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + }) .build(); Status<List<Double>> status = callExecutor.execute(callable); return status.getResult(); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java index 08f4bf7d8..5654b9876 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java @@ -1,7 +1,20 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.fasterxml.jackson.annotation.JsonSetter; - import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -92,7 +105,7 @@ public boolean hasSubsegments() { return !subsegments.isEmpty(); } - public static class SubSegment{ + public static class SubSegment { private String id; private String name; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java index 15026a9d1..7298957aa 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java @@ -1,9 +1,22 @@ -package software.amazon.lambda.powertools.testutils.tracing; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; +package software.amazon.lambda.powertools.testutils.tracing; import java.util.ArrayList; import java.util.List; +import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; public class Trace { private final List<SubSegment> subsegments = new ArrayList<>(); 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 e7cd13640..dc63987fd 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 @@ -1,5 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 static java.time.Duration.ofSeconds; + import com.evanlennick.retry4j.CallExecutor; import com.evanlennick.retry4j.CallExecutorBuilder; import com.evanlennick.retry4j.Status; @@ -8,15 +24,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -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; -import software.amazon.awssdk.services.xray.XRayClient; -import software.amazon.awssdk.services.xray.model.*; -import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; - import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -24,25 +31,42 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.stream.Collectors; - -import static java.time.Duration.ofSeconds; +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; +import software.amazon.awssdk.services.xray.XRayClient; +import software.amazon.awssdk.services.xray.model.BatchGetTracesRequest; +import software.amazon.awssdk.services.xray.model.BatchGetTracesResponse; +import software.amazon.awssdk.services.xray.model.GetTraceSummariesRequest; +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.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 = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 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")); + private static final XRayClient xray = XRayClient.builder() + .httpClient(httpClient) + .region(region) + .build(); private final Instant start; private final Instant end; private final String filterExpression; private final List<String> excludedSegments; /** - * @param start beginning of the time slot to search in - * @param end end of the time slot to search in + * @param start beginning of the time slot to search in + * @param end end of the time slot to search in * @param filterExpression eventual filter for the search * @param excludedSegments list of segment to exclude from the search */ @@ -64,10 +88,11 @@ public static Builder builder() { * @return traces */ public Trace fetchTrace() { - Callable<Trace> callable = () -> { - List<String> traceIds = getTraceIds(); - return getTrace(traceIds); - }; + Callable<Trace> callable = () -> + { + List<String> traceIds = getTraceIds(); + return getTrace(traceIds); + }; RetryConfig retryConfig = new RetryConfigBuilder() .withMaxNumberOfTries(10) @@ -77,7 +102,10 @@ public Trace fetchTrace() { .build(); CallExecutor<Trace> callExecutor = new CallExecutorBuilder<Trace>() .config(retryConfig) - .afterFailedTryListener(s -> {LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries());}) + .afterFailedTryListener(s -> + { + LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + }) .build(); Status<Trace> status = callExecutor.execute(callable); return status.getResult(); @@ -85,6 +113,7 @@ public Trace fetchTrace() { /** * Retrieve traces from trace ids. + * * @param traceIds * @return */ @@ -96,43 +125,49 @@ private Trace getTrace(List<String> traceIds) { throw new RuntimeException("No trace found"); } Trace traceRes = new Trace(); - tracesResponse.traces().forEach(trace -> { - if (trace.hasSegments()) { - 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()); + tracesResponse.traces().forEach(trace -> + { + if (trace.hasSegments()) { + 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()); + } + } + } catch (JsonProcessingException e) { + LOG.error("Failed to parse segment document: " + e.getMessage()); + throw new RuntimeException(e); } - } - } catch (JsonProcessingException e) { - LOG.error("Failed to parse segment document: " + e.getMessage()); - throw new RuntimeException(e); - } - }); - } - }); + }); + } + }); return traceRes; } private void getNestedSubSegments(List<SubSegment> subsegments, Trace traceRes, List<String> idsToIgnore) { - subsegments.forEach(subsegment -> { - List<String> subSegmentIdsToIgnore = Collections.emptyList(); - if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { - traceRes.addSubSegment(subsegment); + subsegments.forEach(subsegment -> + { + List<String> subSegmentIdsToIgnore = Collections.emptyList(); + if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { + traceRes.addSubSegment(subsegment); + if (subsegment.hasSubsegments()) { + subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId) + .collect(Collectors.toList()); + } + } if (subsegment.hasSubsegments()) { - subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId).collect(Collectors.toList()); + getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); } - } - if (subsegment.hasSubsegments()) { - getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); - } - }); + }); } /** * Use the X-Ray SDK to retrieve the trace ids corresponding to a specific function during a specific time slot + * * @return a list of trace ids */ private List<String> getTraceIds() { @@ -146,20 +181,14 @@ private List<String> getTraceIds() { if (!traceSummaries.hasTraceSummaries()) { throw new RuntimeException("No trace id found"); } - 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"); } return traceIds; } - 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 final XRayClient xray = XRayClient.builder() - .httpClient(httpClient) - .region(region) - .build(); - public static class Builder { private Instant start; private Instant end; @@ -167,12 +196,15 @@ public static class Builder { private List<String> excludedSegments = Arrays.asList("Initialization", "Invocation", "Overhead"); public TraceFetcher build() { - if (filterExpression == null) + if (filterExpression == null) { throw new IllegalArgumentException("filterExpression or functionName is required"); - if (start == null) + } + if (start == null) { throw new IllegalArgumentException("start is required"); - if (end == null) + } + 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); } @@ -194,6 +226,7 @@ public Builder filterExpression(String filterExpression) { /** * "Initialization", "Invocation", "Overhead" are excluded by default + * * @param excludedSegments * @return */ @@ -203,7 +236,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-idempotency/pom.xml b/powertools-idempotency/pom.xml index cb4d9b802..ba4b147ae 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -180,6 +194,10 @@ </archive> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + </plugin> </plugins> </build> <repositories> diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java index 28c6f58aa..0d19fa7a9 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; public class Constants { 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 index ce652791b..6da826c45 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; @@ -38,29 +39,6 @@ public class Idempotency { private Idempotency() { } - public IdempotencyConfig getConfig() { - return config; - } - - public BasePersistenceStore getPersistenceStore() { - if (persistenceStore == null) { - throw new IllegalStateException("Persistence Store is null, did you call 'configure()'?"); - } - return persistenceStore; - } - - private void setConfig(IdempotencyConfig config) { - this.config = config; - } - - private void setPersistenceStore(BasePersistenceStore persistenceStore) { - this.persistenceStore = persistenceStore; - } - - private static class Holder { - private final static Idempotency instance = new Idempotency(); - } - public static Idempotency getInstance() { return Holder.instance; } @@ -84,6 +62,29 @@ 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; @@ -94,7 +95,8 @@ public static class Config { */ public void configure() { if (store == null) { - throw new IllegalStateException("Persistence Layer is null, configure one with 'withPersistenceStore()'"); + throw new IllegalStateException( + "Persistence Layer is null, configure one with 'withPersistenceStore()'"); } if (config == null) { config = IdempotencyConfig.builder().build(); diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java index 28f20ffa9..58d0a7f5b 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,12 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; -import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; - import java.time.Duration; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; /** * Configuration of the idempotency feature. Use the {@link Builder} to create an instance. @@ -31,7 +31,9 @@ public class IdempotencyConfig { private final String hashFunction; private Context lambdaContext; - private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, long expirationInSeconds, String hashFunction) { + private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, + boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, + long expirationInSeconds, String hashFunction) { this.localCacheMaxItems = localCacheMaxItems; this.useLocalCache = useLocalCache; this.expirationInSeconds = expirationInSeconds; @@ -41,6 +43,15 @@ private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESP this.hashFunction = hashFunction; } + /** + * Create a builder that can be used to configure and create a {@link IdempotencyConfig}. + * + * @return a new instance of {@link Builder} + */ + public static Builder builder() { + return new Builder(); + } + public int getLocalCacheMaxItems() { return localCacheMaxItems; } @@ -69,24 +80,14 @@ public String getHashFunction() { return hashFunction; } - - /** - * Create a builder that can be used to configure and create a {@link IdempotencyConfig}. - * - * @return a new instance of {@link Builder} - */ - public static Builder builder() { - return new Builder(); + public Context getLambdaContext() { + return lambdaContext; } public void setLambdaContext(Context lambdaContext) { this.lambdaContext = lambdaContext; } - public Context getLambdaContext() { - return lambdaContext; - } - public static class Builder { private int localCacheMaxItems = 256; @@ -107,6 +108,7 @@ public static class Builder { * <pre> * Idempotency.config().withConfig(config).configure(); * </pre> + * * @return an instance of {@link IdempotencyConfig}. */ public IdempotencyConfig build() { @@ -124,16 +126,15 @@ public IdempotencyConfig build() { * 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> - * <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> - * <li><code>detail</code> for ScheduledEvent (EventBridge / CloudWatch events)</li> - * <li><code>Records[*].kinesis.powertools_json(powertools_base64(data))</code> for KinesisEvent</li> - * <li><code>Records[*].powertools_json(powertools_base64(data))</code> for KinesisFirehoseEvent</li> - * <li>...</li> + * <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> + * <li><code>detail</code> for ScheduledEvent (EventBridge / CloudWatch events)</li> + * <li><code>Records[*].kinesis.powertools_json(powertools_base64(data))</code> for KinesisEvent</li> + * <li><code>Records[*].powertools_json(powertools_base64(data))</code> for KinesisFirehoseEvent</li> + * <li>...</li> * </ul> * - * * @param eventKeyJMESPath path of the key in the Lambda event * @return the instance of the builder (to chain operations) */ diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java index 92a0a3d49..4f63b10f5 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import java.lang.annotation.ElementType; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java index e7cace1fb..6ca40a0e1 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,10 +11,10 @@ * limitations under the License. * */ + 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/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java index 3d5ee93c5..dc87f422b 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java index 0d3844641..9e85f4b5f 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java index 40c90dcab..e41e30e84 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java index 088db59c0..ba7da69bf 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java index afae2554e..420829363 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** * Exception thrown when the item was not found in the persistence store. */ -public class IdempotencyItemNotFoundException extends RuntimeException{ +public class IdempotencyItemNotFoundException extends RuntimeException { private static final long serialVersionUID = 4818288566747993032L; public IdempotencyItemNotFoundException(String idempotencyKey) { - super("Item with idempotency key "+ idempotencyKey + " not found"); + super("Item with idempotency key " + idempotencyKey + " not found"); } } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java index 7259dff0f..29b8bd2ec 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java index fa49b746c..bfdd2d792 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java index 5aee228eb..cdb2bb6a7 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 5ce723f04..2875ab3d1 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,26 +11,32 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal; +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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.idempotency.Idempotency; -import software.amazon.lambda.powertools.idempotency.exceptions.*; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyAlreadyInProgressException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyInconsistentStateException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyPersistenceLayerException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Instant; -import java.util.OptionalInt; - -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; - /** * 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. @@ -90,7 +96,9 @@ private Object processIdempotency() throws Throwable { } catch (IdempotencyKeyException ike) { throw ike; } 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.", 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.", + e); } return getFunctionResponse(); } @@ -121,11 +129,14 @@ private DataRecord getIdempotencyRecord() { } catch (IdempotencyItemNotFoundException e) { // This code path will only be triggered if the record is removed between saveInProgress and getRecord LOG.debug("An existing idempotency record was deleted before we could fetch it"); - throw new IdempotencyInconsistentStateException("saveInProgress and getRecord return inconsistent results", e); + throw new IdempotencyInconsistentStateException("saveInProgress and getRecord return inconsistent results", + e); } catch (IdempotencyValidationException | IdempotencyKeyException vke) { throw vke; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to get record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to get record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } } @@ -144,19 +155,24 @@ private Object handleForStatus(DataRecord record) { if (INPROGRESS.equals(record.getStatus())) { if (record.getInProgressExpiryTimestamp().isPresent() && record.getInProgressExpiryTimestamp().getAsLong() < Instant.now().toEpochMilli()) { - throw new IdempotencyInconsistentStateException("Item should have been expired in-progress because it already time-outed."); + throw new IdempotencyInconsistentStateException( + "Item should have been expired in-progress because it already time-outed."); } - throw new IdempotencyAlreadyInProgressException("Execution already in progress with idempotency key: " + record.getIdempotencyKey()); + throw new IdempotencyAlreadyInProgressException( + "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)) + LOG.debug("Response for key '{}' retrieved from idempotency store, skipping the function", + record.getIdempotencyKey()); + if (returnType.equals(String.class)) { return record.getResponseData(); + } return JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), returnType); } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Unable to get function response as " + returnType.getSimpleName(), e); + throw new IdempotencyPersistenceLayerException( + "Unable to get function response as " + returnType.getSimpleName(), e); } } @@ -172,7 +188,9 @@ private Object getFunctionResponse() throws Throwable { } catch (IdempotencyKeyException ke) { throw ke; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to delete record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to delete record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } throw handlerException; } @@ -180,7 +198,9 @@ private Object getFunctionResponse() throws Throwable { try { persistenceStore.saveSuccess(data, response, Instant.now()); } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to update record state to success in idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to update record state to success in idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } return response; } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index dc2703e64..d34dd72dd 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal; +import static software.amazon.lambda.powertools.core.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.Pointcut; import org.aspectj.lang.reflect.MethodSignature; -import com.amazonaws.services.lambda.runtime.Context; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyKey; @@ -27,11 +32,6 @@ import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; - /** * Aspect that handles the {@link Idempotent} annotation. * It uses the {@link IdempotencyHandler} to actually do the job. @@ -48,19 +48,21 @@ public Object around(ProceedingJoinPoint pjp, Idempotent idempotent) throws Throwable { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); - if (idempotencyDisabledEnv != null && !idempotencyDisabledEnv.equalsIgnoreCase("false")) { + if (idempotencyDisabledEnv != null && !"false".equalsIgnoreCase(idempotencyDisabledEnv)) { return pjp.proceed(pjp.getArgs()); } Method method = ((MethodSignature) pjp.getSignature()).getMethod(); if (method.getReturnType().equals(void.class)) { - throw new IdempotencyConfigurationException("The annotated method doesn't return anything. Unable to perform idempotency on void return type"); + throw new IdempotencyConfigurationException( + "The annotated method doesn't return anything. Unable to perform idempotency on void return type"); } boolean isHandler = placedOnRequestHandler(pjp); JsonNode payload = getPayload(pjp, method, isHandler); if (payload == null) { - throw new IdempotencyConfigurationException("Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); + throw new IdempotencyConfigurationException( + "Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); } Context lambdaContext; @@ -76,7 +78,8 @@ public Object around(ProceedingJoinPoint pjp, /** * Retrieve the payload from the annotated method parameters - * @param pjp joinPoint + * + * @param pjp joinPoint * @param method the annotated method * @return the payload used for idempotency */ diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java index a017c211a..b57ad2977 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal.cache; import java.util.LinkedHashMap; @@ -19,6 +20,7 @@ /** * Implementation of a simple LRU Cache based on a {@link LinkedHashMap} * See <a href="https://stackoverflow.com/a/6400874/270653">here</a>. + * * @param <K> Type of the keys * @param <V> Types of the values */ diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index c79068d1a..f58b276fd 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,23 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import static software.amazon.lambda.powertools.core.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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.utils.StringUtils; -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.exceptions.IdempotencyKeyException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; -import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; -import software.amazon.lambda.powertools.utilities.JsonConfig; - import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -42,8 +34,16 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; - -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.utils.StringUtils; +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.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.utilities.JsonConfig; /** * Persistence layer that will store the idempotency result. @@ -53,7 +53,7 @@ public abstract class BasePersistenceStore implements PersistenceStore { private static final Logger LOG = LoggerFactory.getLogger(BasePersistenceStore.class); - + protected boolean payloadValidationEnabled = false; private String functionName = ""; private boolean configured = false; private long expirationInSeconds = 60 * 60; // 1 hour default @@ -61,7 +61,6 @@ public abstract class BasePersistenceStore implements PersistenceStore { private LRUCache<String, DataRecord> cache; private String eventKeyJMESPath; private Expression<JsonNode> eventKeyCompiledJMESPath; - protected boolean payloadValidationEnabled = false; private Expression<JsonNode> validationKeyJMESPath; private boolean throwOnNoIdempotencyKey = false; private String hashFunctionName; @@ -130,7 +129,8 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { responseJson, getHashedPayload(data) ); - LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", record.getIdempotencyKey()); + LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", + record.getIdempotencyKey()); updateRecord(record); saveToCache(record); } catch (JsonProcessingException e) { @@ -145,7 +145,8 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { * @param data Payload * @param now */ - public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTimeInMs) throws IdempotencyItemAlreadyExistsException { + public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTimeInMs) + throws IdempotencyItemAlreadyExistsException { Optional<String> hashedIdempotencyKey = getHashedIdempotencyKey(data); if (!hashedIdempotencyKey.isPresent()) { // missing idempotency key => non-idempotent transaction, we do not store the data, simply return @@ -159,7 +160,8 @@ 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( @@ -205,7 +207,8 @@ public void deleteRecord(JsonNode data, Throwable throwable) { * @throws IdempotencyValidationException Payload doesn't match the stored record for the given idempotency key * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key */ - public DataRecord getRecord(JsonNode data, Instant now) throws IdempotencyValidationException, IdempotencyItemNotFoundException { + public DataRecord getRecord(JsonNode data, Instant now) + throws IdempotencyValidationException, IdempotencyItemNotFoundException { Optional<String> hashedIdempotencyKey = getHashedIdempotencyKey(data); if (!hashedIdempotencyKey.isPresent()) { // missing idempotency key => non-idempotent transaction, we do not get the data, simply return nothing @@ -255,7 +258,9 @@ 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); + Stream<Map.Entry<String, JsonNode>> stream = + StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.fields(), Spliterator.ORDERED), + false); return stream.allMatch(e -> e.getValue().isNull()); } return data.isNull(); @@ -302,7 +307,9 @@ String generateHash(JsonNode data) { node = data.decimalValue(); } else if (data.isBoolean()) { node = data.asBoolean(); - } else node = data; // anything else + } else { + node = data; // anything else + } MessageDigest hashAlgorithm = getHashAlgorithm(); byte[] digest = hashAlgorithm.digest(node.toString().getBytes(StandardCharsets.UTF_8)); @@ -356,17 +363,20 @@ private long getExpiryEpochSecond(Instant now) { * @param dataRecord DataRecord to save in cache */ private void saveToCache(DataRecord dataRecord) { - if (!useLocalCache) + if (!useLocalCache) { return; - if (dataRecord.getStatus().equals(DataRecord.Status.INPROGRESS)) + } + if (dataRecord.getStatus().equals(DataRecord.Status.INPROGRESS)) { return; + } cache.put(dataRecord.getIdempotencyKey(), dataRecord); } private DataRecord retrieveFromCache(String idempotencyKey, Instant now) { - if (!useLocalCache) + if (!useLocalCache) { return null; + } DataRecord record = cache.get(idempotencyKey); if (record != null) { @@ -380,8 +390,9 @@ private DataRecord retrieveFromCache(String idempotencyKey, Instant now) { } private void deleteFromCache(String idempotencyKey) { - if (!useLocalCache) + if (!useLocalCache) { return; + } cache.remove(idempotencyKey); } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java index 54001c449..9af7c6a53 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.idempotency.persistence; -import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +package software.amazon.lambda.powertools.idempotency.persistence; import java.time.Instant; import java.util.Objects; -import java.util.OptionalInt; 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. @@ -30,7 +29,7 @@ public class DataRecord { /** * This field is controlling how long the result of the idempotent * event is cached. It is stored in _seconds since epoch_. - * + * <p> * DynamoDB's TTL mechanism is used to remove the record once the * expiry has been reached, and subsequent execution of the request * will be permitted. The user must configure this on their table. @@ -43,16 +42,17 @@ public class DataRecord { * The in-progress field is set to the remaining lambda execution time * when the record is created. * This field is stored in _milliseconds since epoch_. - * + * <p> * This ensures that: - * + * <p> * 1/ other concurrently executing requests are blocked from starting * 2/ if a lambda times out, subsequent requests will be allowed again, despite - * the fact that the idempotency record is already in the table + * the fact that the idempotency record is already in the table */ private final OptionalLong inProgressExpiryTimestamp; - public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash) { + public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, + String payloadHash) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -61,7 +61,8 @@ public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, St this.inProgressExpiryTimestamp = OptionalLong.empty(); } - public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash, OptionalLong inProgressExpiryTimestamp) { + public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, + String payloadHash, OptionalLong inProgressExpiryTimestamp) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -110,8 +111,12 @@ public String getPayloadHash() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } DataRecord record = (DataRecord) o; return expiryTimestamp == record.expiryTimestamp && idempotencyKey.equals(record.idempotencyKey) diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 783b029bb..7a023b4de 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,31 +11,37 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +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.slf4j.Logger; import org.slf4j.LoggerFactory; 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.*; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +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.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; -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 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; - /** * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.<br> * Use the {@link Builder} to create a new instance. @@ -83,7 +89,7 @@ private DynamoDBPersistenceStore(String tableName, this.dynamoDbClient = client; } else { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); - if (idempotencyDisabledEnv == null || idempotencyDisabledEnv.equalsIgnoreCase("false")) { + if (idempotencyDisabledEnv == null || "false".equalsIgnoreCase(idempotencyDisabledEnv)) { this.dynamoDbClient = DynamoDbClient.builder() .httpClient(UrlConnectionHttpClient.builder().build()) .region(Region.of(System.getenv(AWS_REGION_ENV))) @@ -96,6 +102,10 @@ private DynamoDBPersistenceStore(String tableName, } } + public static Builder builder() { + return new Builder(); + } + @Override public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { GetItemResponse response = dynamoDbClient.getItem( @@ -133,7 +143,9 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre item.put(this.statusAttr, AttributeValue.builder().s(record.getStatus().toString()).build()); if (record.getInProgressExpiryTimestamp().isPresent()) { - item.put(this.inProgressExpiryAttr, AttributeValue.builder().n(String.valueOf(record.getInProgressExpiryTimestamp().getAsLong())).build()); + item.put(this.inProgressExpiryAttr, + AttributeValue.builder().n(String.valueOf(record.getInProgressExpiryTimestamp().getAsLong())) + .build()); } if (this.payloadValidationEnabled) { @@ -151,9 +163,12 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map<String, AttributeValue> expressionAttributeValues = Stream.of( - new AbstractMap.SimpleEntry<>(":now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()), - new AbstractMap.SimpleEntry<>(":now_milliseconds", AttributeValue.builder().n(String.valueOf(now.toEpochMilli())).build()), - new AbstractMap.SimpleEntry<>(":inprogress", AttributeValue.builder().s(INPROGRESS.toString()).build()) + new AbstractMap.SimpleEntry<>(":now", + AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()), + new AbstractMap.SimpleEntry<>(":now_milliseconds", + AttributeValue.builder().n(String.valueOf(now.toEpochMilli())).build()), + new AbstractMap.SimpleEntry<>(":inprogress", + AttributeValue.builder().s(INPROGRESS.toString()).build()) ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -161,14 +176,16 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre PutItemRequest.builder() .tableName(tableName) .item(item) - .conditionExpression("attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now_milliseconds AND #status = :inprogress)") + .conditionExpression( + "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() ); } catch (ConditionalCheckFailedException e) { LOG.debug("Failed to put record for already existing idempotency key: {}", record.getIdempotencyKey()); - throw new IdempotencyItemAlreadyExistsException("Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); + throw new IdempotencyItemAlreadyExistsException( + "Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); } } @@ -184,15 +201,19 @@ public void updateRecord(DataRecord record) { .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map<String, AttributeValue> expressionAttributeValues = Stream.of( - new AbstractMap.SimpleEntry<>(":response_data", AttributeValue.builder().s(record.getResponseData()).build()), - new AbstractMap.SimpleEntry<>(":expiry", AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()), - new AbstractMap.SimpleEntry<>(":status", AttributeValue.builder().s(record.getStatus().toString()).build())) + new AbstractMap.SimpleEntry<>(":response_data", + AttributeValue.builder().s(record.getResponseData()).build()), + new AbstractMap.SimpleEntry<>(":expiry", + AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()), + new AbstractMap.SimpleEntry<>(":status", + AttributeValue.builder().s(record.getStatus().toString()).build())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if (payloadValidationEnabled) { updateExpression += ", #validation_key = :validation_key"; expressionAttributeNames.put("#validation_key", this.validationAttr); - expressionAttributeValues.put(":validation_key", AttributeValue.builder().s(record.getPayloadHash()).build()); + expressionAttributeValues.put(":validation_key", + AttributeValue.builder().s(record.getPayloadHash()).build()); } dynamoDbClient.updateItem(UpdateItemRequest.builder() @@ -242,16 +263,14 @@ private DataRecord itemToRecord(Map<String, AttributeValue> item) { // data and validation payload may be null AttributeValue data = item.get(this.dataAttr); AttributeValue validation = item.get(this.validationAttr); - return new DataRecord(item.get(sortKeyAttr != null ? sortKeyAttr: keyAttr).s(), + return new DataRecord(item.get(sortKeyAttr != null ? sortKeyAttr : keyAttr).s(), DataRecord.Status.valueOf(item.get(this.statusAttr).s()), Long.parseLong(item.get(this.expiryAttr).n()), data != null ? data.s() : null, validation != null ? validation.s() : null, - item.get(this.inProgressExpiryAttr) != null ? OptionalLong.of(Long.parseLong(item.get(this.inProgressExpiryAttr).n())) : OptionalLong.empty()); - } - - public static Builder builder() { - return new Builder(); + item.get(this.inProgressExpiryAttr) != null ? + OptionalLong.of(Long.parseLong(item.get(this.inProgressExpiryAttr).n())) : + OptionalLong.empty()); } /** @@ -288,7 +307,8 @@ public DynamoDBPersistenceStore build() { if (StringUtils.isEmpty(tableName)) { throw new IllegalArgumentException("Table name is not specified"); } - return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, inProgressExpiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); + return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, + inProgressExpiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); } /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java index d199c99b5..c058b592e 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,13 @@ * limitations under the License. * */ + 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}. @@ -26,6 +26,7 @@ public interface PersistenceStore { /** * Retrieve item from persistence store using idempotency key and return it as a DataRecord instance. + * * @param idempotencyKey the key of the record * @return DataRecord representation of existing record found in persistence store * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key @@ -34,6 +35,7 @@ public interface PersistenceStore { /** * Add a DataRecord to persistence store if it does not already exist with that key + * * @param record DataRecord instance * @param now * @throws IdempotencyItemAlreadyExistsException if a non-expired entry already exists. @@ -42,12 +44,14 @@ public interface PersistenceStore { /** * Update item in persistence store + * * @param record DataRecord instance */ void updateRecord(DataRecord record); /** * Remove item from persistence store + * * @param idempotencyKey the key of the record */ void deleteRecord(String idempotencyKey); 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 index 38678322c..66ddb53ac 100644 --- 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 @@ -1,7 +1,24 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; @@ -9,11 +26,14 @@ 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.*; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; +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"; @@ -24,7 +44,7 @@ public class DynamoDBConfig { public static void setupDynamo() { int port = getFreePort(); try { - dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[]{ + dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[] { "-inMemory", "-port", Integer.toString(port) @@ -51,7 +71,8 @@ public static void setupDynamo() { .billingMode(BillingMode.PAY_PER_REQUEST) .build()); - DescribeTableResponse response = client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); + DescribeTableResponse response = + client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); if (response == null) { throw new RuntimeException("Table was not created within expected time"); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java index a782d9613..c94fec3db 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java @@ -1,6 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; @@ -11,8 +27,6 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyFunction; -import static org.assertj.core.api.Assertions.assertThat; - public class IdempotencyTest extends DynamoDBConfig { @Mock @@ -27,12 +41,14 @@ void setUp() { public 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/IdempotencyEnabledFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java index 6c39dc6de..f12edc87e 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java index 0423bd90a..76c36ae9f 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java @@ -1,9 +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.handlers; 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; @@ -13,14 +34,6 @@ import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; import software.amazon.lambda.powertools.utilities.JsonConfig; -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; - public class IdempotencyFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java index f3c1bdbc9..34e3eb319 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -38,14 +39,14 @@ public Basket handleRequest(Product input, Context context) { if (registerContext) { Idempotency.registerLambdaContext(context); } - + return createBasket("fake", input); } @Idempotent private Basket createBasket(@IdempotencyKey String magicProduct, Product p) { called = true; - Basket b = new Basket(p); + Basket b = new Basket(p); b.add(new Product(0, magicProduct, 0)); return b; } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java index 566db6727..3ae500341 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java index 4c82bff15..42e438798 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -33,7 +34,7 @@ public Basket handleRequest(Product input, Context context) { @Idempotent private Basket createBasket(String magicProduct, Product p) { - Basket b = new Basket(p); + Basket b = new Basket(p); b.add(new Product(0, magicProduct, 0)); return b; } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java index a6b89fc8d..384ed5e86 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -28,7 +29,7 @@ public class IdempotencyInternalFunctionVoid implements RequestHandler<Product, @Override public Basket handleRequest(Product input, Context context) { - Basket b = new Basket(input); + Basket b = new Basket(input); addProduct("fake", b); return b; } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java index e6d719598..3636657aa 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java index 1444d8a5f..f46576033 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java index 56687e338..9a30472a8 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,26 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal; +import static java.time.temporal.ChronoUnit.SECONDS; +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.doReturn; +import static org.mockito.Mockito.doThrow; +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.junitpioneer.jupiter.SetEnvironmentVariable; @@ -29,23 +44,19 @@ import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyInconsistentStateException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; -import software.amazon.lambda.powertools.idempotency.handlers.*; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyEnabledFunction; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyInternalFunction; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyInternalFunctionInternalKey; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyInternalFunctionInvalid; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyInternalFunctionVoid; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyStringFunction; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyWithErrorFunction; 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.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Instant; -import java.util.OptionalInt; -import java.util.OptionalLong; - -import static java.time.temporal.ChronoUnit.SECONDS; -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.*; - public class IdempotencyAspectTest { @Mock @@ -153,7 +164,8 @@ public void secondCall_notExpired_shouldGetStringFromStore() { } @Test - public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() throws JsonProcessingException { + public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() + throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -166,7 +178,8 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti 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) + OptionalLong timestampInFuture = + OptionalLong.of(Instant.now().toEpochMilli() + 1000); // timeout not expired (in 1sec) DataRecord record = new DataRecord( "42", DataRecord.Status.INPROGRESS, @@ -178,11 +191,13 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti // THEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyAlreadyInProgressException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyAlreadyInProgressException.class); } @Test - public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() throws JsonProcessingException { + public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() + throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -195,7 +210,8 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons 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 + OptionalLong timestampInThePast = + OptionalLong.of(Instant.now().toEpochMilli() - 100); // timeout expired 100ms ago DataRecord record = new DataRecord( "42", DataRecord.Status.INPROGRESS, @@ -207,7 +223,8 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons // THEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyInconsistentStateException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyInconsistentStateException.class); } @Test @@ -278,7 +295,8 @@ public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { ArgumentCaptor<Basket> resultCaptor = ArgumentCaptor.forClass(Basket.class); verify(store).saveSuccess(any(), resultCaptor.capture(), any()); - assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), new Product(0, "fake", 0)); + assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), + new Product(0, "fake", 0)); } @Test @@ -305,11 +323,13 @@ public void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shoul ArgumentCaptor<Basket> resultCaptor = ArgumentCaptor.forClass(Basket.class); verify(store).saveSuccess(any(), resultCaptor.capture(), any()); - assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), new Product(0, "fake", 0)); + assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), + new Product(0, "fake", 0)); } @Test - public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { + public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() + throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -354,7 +374,8 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey( ArgumentCaptor<DataRecord> recordCaptor = ArgumentCaptor.forClass(DataRecord.class); verify(persistenceStore).putRecord(recordCaptor.capture(), any()); // a1d0c6e83f027327d8461063f4ac58a6 = MD5(42) - assertThat(recordCaptor.getValue().getIdempotencyKey()).isEqualTo("testFunction.createBasket#a1d0c6e83f027327d8461063f4ac58a6"); + assertThat(recordCaptor.getValue().getIdempotencyKey()).isEqualTo( + "testFunction.createBasket#a1d0c6e83f027327d8461063f4ac58a6"); } @Test @@ -369,7 +390,8 @@ public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { Product p = new Product(42, "fake product", 12); // THEN - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyConfigurationException.class); } @Test @@ -384,7 +406,8 @@ public void idempotencyOnSubMethodVoid_shouldThrowException() { Product p = new Product(42, "fake product", 12); // THEN - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyConfigurationException.class); } } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java index 3d2f7c7e3..8854be1f2 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.idempotency.internal.cache; -import org.junit.jupiter.api.Test; +package software.amazon.lambda.powertools.idempotency.internal.cache; import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + public class LRUCacheTest { @Test diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java index 304fd3810..a17bb8abf 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.model; import java.util.ArrayList; @@ -21,19 +22,19 @@ public class Basket { private List<Product> products = new ArrayList<>(); - public List<Product> getProducts() { - return products; + public Basket() { } - public void setProducts(List<Product> products) { - this.products = products; + public Basket(Product... p) { + products.addAll(Arrays.asList(p)); } - public Basket() { + public List<Product> getProducts() { + return products; } - public Basket( Product ...p){ - products.addAll(Arrays.asList(p)); + public void setProducts(List<Product> products) { + this.products = products; } public void add(Product product) { @@ -42,8 +43,12 @@ public void add(Product product) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Basket basket = (Basket) o; return products.equals(basket.products); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java index 1c66c584d..7fa191d82 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.model; import java.util.Objects; @@ -57,8 +58,12 @@ public void setPrice(double price) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + 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); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index 6b58fa8a5..67bc5aa22 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,21 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +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 software.amazon.lambda.powertools.idempotency.IdempotencyConfig; @@ -29,14 +37,6 @@ import software.amazon.lambda.powertools.idempotency.model.Product; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.OptionalInt; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - public class BasePersistenceStoreTest { private DataRecord dr; @@ -53,7 +53,8 @@ public void setup() { @Override public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { status = 0; - return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", validationHash); + return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, + Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", validationHash); } @Override @@ -84,7 +85,8 @@ public void saveInProgress_defaultConfig() { persistenceStore.configure(IdempotencyConfig.builder().build(), null); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); @@ -101,13 +103,15 @@ public void saveInProgress_withRemainingTime() { int lambdaTimeoutMs = 30000; Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.of(lambdaTimeoutMs)); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.of(lambdaTimeoutMs)); 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.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo(now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); + assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo( + now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); assertThat(status).isEqualTo(1); } @@ -119,7 +123,8 @@ public void saveInProgress_jmespath() { .build(), "myfunc"); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); @@ -136,7 +141,9 @@ public void saveInProgress_jmespath_NotFound_shouldThrowException() { .withThrowOnNoIdempotencyKey(true) // should throw .build(), ""); Instant now = Instant.now(); - assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) + assertThatThrownBy( + () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty())) .isInstanceOf(IdempotencyKeyException.class) .hasMessageContaining("No data found to create a hashed idempotency key"); assertThat(status).isEqualTo(-1); @@ -149,7 +156,8 @@ public void saveInProgress_jmespath_NotFound_shouldNotPersist() { .withEventKeyJMESPath("unavailable") .build(), ""); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr).isNull(); assertThat(status).isEqualTo(-1); } @@ -170,7 +178,9 @@ public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), null, null) ); - assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) + assertThatThrownBy( + () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty())) .isInstanceOf(IdempotencyItemAlreadyExistsException.class); assertThat(status).isEqualTo(-1); } @@ -192,7 +202,8 @@ public void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), null, null) ); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(cache).isEmpty(); assertThat(status).isEqualTo(1); @@ -250,7 +261,8 @@ public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessi //<editor-fold desc="getRecord"> @Test - public void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + public 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); @@ -264,7 +276,8 @@ public void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItem } @Test - public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() + throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -287,7 +300,8 @@ public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throw } @Test - public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() + throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache<String, DataRecord> cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -314,14 +328,15 @@ public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() th public 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())) + assertThatThrownBy( + () -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) .isInstanceOf(IdempotencyValidationException.class); } @@ -348,7 +363,8 @@ public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { .withUseLocalCache(true).build(), null, cache); cache.put("testFunction#7b40f56c086de5aa91dc467456329ed2", - new DataRecord("testFunction#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, 123, null, null)); + new DataRecord("testFunction#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, 123, null, + null)); persistenceStore.deleteRecord(JsonConfig.get().getObjectMapper().valueToTree(event), new ArithmeticException()); assertThat(status).isEqualTo(3); assertThat(cache).isEmpty(); @@ -385,7 +401,8 @@ public void generateHashDouble_shouldGenerateMd5ofDouble() { @Test public 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); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java index 768da2eaa..b19cebfe1 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,28 +11,39 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +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.*; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.BillingMode; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +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 java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - /** * 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 @@ -51,7 +62,8 @@ public void putRecord_shouldCreateRecordInDynamoDB() throws IdempotencyItemAlrea 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)); @@ -81,7 +93,8 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfPreviousExpired() { ), 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)); @@ -113,7 +126,8 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfLambdaWasInProgressAndTimed ), 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)); @@ -144,7 +158,8 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA ).isInstanceOf(IdempotencyItemAlreadyExistsException.class); // 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)); @@ -178,7 +193,8 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire .isInstanceOf(IdempotencyItemAlreadyExistsException.class); // 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)); @@ -217,7 +233,8 @@ public void getRecord_shouldReturnExistingRecord() throws IdempotencyItemNotFoun @Test public void getRecord_shouldThrowException_whenRecordIsAbsent() { - assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")).isInstanceOf(IdempotencyItemNotFoundException.class); + assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")).isInstanceOf( + IdempotencyItemNotFoundException.class); } //</editor-fold> @@ -237,7 +254,8 @@ public void updateRecord_shouldUpdateRecord() { item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); // enable payload validation - dynamoDBPersistenceStore.configure(IdempotencyConfig.builder().withPayloadValidationJMESPath("path").build(), null); + dynamoDBPersistenceStore.configure(IdempotencyConfig.builder().withPayloadValidationJMESPath("path").build(), + null); // WHEN expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); @@ -245,7 +263,8 @@ public void updateRecord_shouldUpdateRecord() { dynamoDBPersistenceStore.updateRecord(record); // 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"); @@ -290,8 +309,10 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou 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() + AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.S) + .build(), + AttributeDefinition.builder().attributeName("sortkey").attributeType(ScalarAttributeType.S) + .build() ) .billingMode(BillingMode.PAY_PER_REQUEST) .build()); @@ -323,7 +344,8 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou 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"); diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 767fbd3ee..83650fcde 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -127,4 +141,12 @@ </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-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java index e0d24b8a5..ce43c9aa0 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/CorrelationIdPathConstants.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.logging; /** @@ -11,7 +25,7 @@ public class CorrelationIdPathConstants { /** * 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 */ @@ -19,5 +33,5 @@ public class CorrelationIdPathConstants { /** * To use when function is expecting Event Bridge Request event */ - public static final String EVENT_BRIDGE = "/id"; + public static final String EVENT_BRIDGE = "/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 b86b800b7..9932eb700 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging; import java.lang.annotation.ElementType; @@ -70,7 +71,8 @@ /** * Json Pointer path to extract correlation id from. - * @see <a href=https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-03/> + * + * @see <a href=https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-03/> */ String correlationIdPath() default ""; 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 index 9a1567d57..6e11573cc 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,18 +11,18 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.logging.log4j.ThreadContext; +import static java.util.Arrays.asList; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; - -import static java.util.Arrays.asList; +import org.apache.logging.log4j.ThreadContext; /** * A class of helper functions to add additional functionality to Logging. - * + * <p> * {@see Logging} */ public final class LoggingUtils { @@ -35,7 +35,7 @@ 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 key The name of the key to be logged * @param value The value to be logged */ public static void appendKey(String key, String value) { @@ -43,7 +43,6 @@ public static void appendKey(String key, String value) { } - /** * Appends additional key and value to each log entry made. Duplicate values * for the same key will be replaced with the latest. @@ -93,8 +92,8 @@ public static void defaultObjectMapper(ObjectMapper objectMapper) { } public static ObjectMapper objectMapper() { - if(null == objectMapper) { - objectMapper = new ObjectMapper(); + if (null == objectMapper) { + objectMapper = new ObjectMapper(); } return objectMapper; 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 index c96d1383e..f98e2ee46 100644 --- 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.logging.internal; import com.fasterxml.jackson.annotation.JsonAnyGetter; @@ -7,6 +21,11 @@ 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; @@ -26,17 +45,145 @@ import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.Strings; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.LinkedHashMap; -import java.util.Map; - @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> { @@ -81,80 +228,68 @@ public boolean getEventEol() { return eventEol; } - public String getEndOfLine() { - return endOfLine; - } - - public boolean isCompact() { - return compact; - } - - public boolean isComplete() { - return complete; - } - - public boolean isLocationInfo() { - return locationInfo; - } - - public boolean isProperties() { - return properties; - } - - /** - * 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; - } - - public boolean isStacktraceAsString() { - return stacktraceAsString; - } - - public boolean isIncludeNullDelimiter() { return includeNullDelimiter; } - - public boolean isIncludeTimeMillis() { - return includeTimeMillis; - } - - public KeyValuePair[] getAdditionalFields() { - return additionalFields; - } - 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 */ @@ -163,6 +298,10 @@ public B setIncludeStacktrace(final boolean 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). * @@ -173,6 +312,10 @@ public B setStacktraceAsString(final boolean stacktraceAsString) { return asBuilder(); } + public boolean isIncludeNullDelimiter() { + return includeNullDelimiter; + } + /** * Whether to include NULL byte as delimiter after each event (optional, default to false). * @@ -183,6 +326,10 @@ public B setIncludeNullDelimiter(final boolean includeNullDelimiter) { return asBuilder(); } + public boolean isIncludeTimeMillis() { + return includeTimeMillis; + } + /** * Whether to include the timestamp (in addition to the Instant) (optional, default to false). * @@ -193,6 +340,10 @@ public B setIncludeTimeMillis(final boolean includeTimeMillis) { return asBuilder(); } + public KeyValuePair[] getAdditionalFields() { + return additionalFields; + } + /** * Additional fields to set on each log event. * @@ -204,132 +355,6 @@ public B setAdditionalFields(final KeyValuePair[] additionalFields) { } } - 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; - } - - /** - * 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; - } - } - - private static LogEvent convertMutableToLog4jEvent(final LogEvent event) { - return event instanceof Log4jLogEvent ? event : Log4jLogEvent.createMemento(event); - } - - 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(); - } - @JsonRootName(XmlConstants.ELT_EVENT) public static class LogEventWithAdditionalFields { @@ -471,13 +496,13 @@ public boolean isEndOfBatch() { } @Override - public boolean isIncludeLocation() { - return event.isIncludeLocation(); + public void setEndOfBatch(boolean endOfBatch) { + } @Override - public void setEndOfBatch(boolean endOfBatch) { - + public boolean isIncludeLocation() { + return event.isIncludeLocation(); } @Override 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/DefaultLambdaFields.java index a50b292b2..2461ae771 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/DefaultLambdaFields.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,10 +11,10 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.internal; import com.amazonaws.services.lambda.runtime.Context; - import java.util.HashMap; import java.util.Map; @@ -31,10 +31,6 @@ enum DefaultLambdaFields { this.name = name; } - public String getName() { - return name; - } - static Map<String, String> values(Context context) { Map<String, String> hashMap = new HashMap<>(); @@ -46,4 +42,8 @@ static Map<String, String> values(Context context) { return hashMap; } + + public String getName() { + return name; + } } 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 index 41247cfdb..6b568be30 100644 --- 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.logging.internal; import com.fasterxml.jackson.core.PrettyPrinter; @@ -7,16 +21,57 @@ 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; -import java.util.HashSet; -import java.util.Set; - @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; @@ -24,7 +79,8 @@ static class JSON extends JacksonFactoryCopy { private final boolean stacktraceAsString; private final boolean objectMessageAsJsonObject; - public JSON(final boolean encodeThreadContextAsList, final boolean includeStacktrace, final boolean stacktraceAsString, 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; @@ -63,7 +119,8 @@ protected PrettyPrinter newCompactPrinter() { @Override protected ObjectMapper newObjectMapper() { - return new Log4jJsonObjectMapper(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, objectMessageAsJsonObject); + return new Log4jJsonObjectMapper(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, + objectMessageAsJsonObject); } @Override @@ -73,45 +130,4 @@ protected PrettyPrinter newPrettyPrinter() { } - 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); - } - } \ No newline at end of file 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 index 578937231..c2c13c86f 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,25 @@ * 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; @@ -30,75 +43,16 @@ import org.apache.logging.log4j.core.util.KeyValuePair; import org.apache.logging.log4j.util.Strings; -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 static java.time.Instant.ofEpochMilli; -import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; - /*** * 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 = "["; - static final String CONTENT_TYPE = "application/json"; - - 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(); - } - } - private LambdaJsonLayout(final Configuration config, final boolean locationInfo, final boolean properties, final boolean encodeThreadContextAsList, final boolean complete, final boolean compact, final boolean eventEol, @@ -106,16 +60,34 @@ private LambdaJsonLayout(final Configuration config, final boolean locationInfo, 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), + 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(), + 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. * @@ -170,21 +142,6 @@ public String getContentType() { return CONTENT_TYPE + "; charset=" + this.getCharset(); } - @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); - } - @Override public Object wrapLogEvent(final LogEvent event) { Map<String, Object> additionalFieldsMap = resolveAdditionalFields(event); @@ -205,15 +162,60 @@ private Map<String, Object> resolveAdditionalFields(LogEvent logEvent) { 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); - } - }); + 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 { @@ -237,7 +239,8 @@ public Map<String, Object> getAdditionalFields() { @JsonGetter("timestamp") public String getTimestamp() { - return ISO_ZONED_DATE_TIME.format(ZonedDateTime.from(ofEpochMilli(logEvent.getTimeMillis()).atZone(ZoneId.systemDefault()))); + 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 d489e093b..4a98735af 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,37 @@ * limitations under the License. * */ + 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 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.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; @@ -32,31 +57,6 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.Map; -import java.util.Optional; -import java.util.Random; - -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; - @Aspect @DeclarePrecedence("*, software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect") public final class LambdaLoggingAspect { @@ -76,6 +76,12 @@ public final class LambdaLoggingAspect { 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(); + } + @SuppressWarnings({"EmptyMethod"}) @Pointcut("@annotation(logging)") public void callAt(Logging logging) { @@ -90,7 +96,7 @@ public Object around(ProceedingJoinPoint pjp, Context extractedContext = extractContext(pjp); - if(null != extractedContext) { + if (null != extractedContext) { appendKeys(DefaultLambdaFields.values(extractedContext)); appendKey("coldStart", isColdStart() ? "true" : "false"); appendKey("service", serviceName()); @@ -108,7 +114,7 @@ public Object around(ProceedingJoinPoint pjp, Object proceed = pjp.proceed(proceedArgs); - if(logging.clearState()) { + if (logging.clearState()) { ThreadContext.clearMap(); } @@ -116,12 +122,6 @@ public Object around(ProceedingJoinPoint pjp, return proceed; } - private static void resetLogLevels(Level logLevel) { - LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - Configurator.setAllLevels(LogManager.getRootLogger().getName(), logLevel); - ctx.updateLoggers(); - } - private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, final Logging logging) { double samplingRate = samplingRate(logging); @@ -129,7 +129,8 @@ private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, if (isHandlerMethod(pjp)) { if (samplingRate < 0 || samplingRate > 1) { - LOG.debug("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", samplingRate); + LOG.debug("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", + samplingRate); return; } 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 index c392e2ed9..c7b7c5d53 100644 --- 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.logging.internal; import org.apache.logging.log4j.core.LogEvent; @@ -25,12 +39,13 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { // Inject all the context information. ReadOnlyStringMap contextData = logEvent.getContextData(); - contextData.forEach((key, value) -> { + contextData.forEach((key, value) -> + { jsonWriter.writeSeparator(); jsonWriter.writeString(key); stringBuilder.append(':'); jsonWriter.writeValue(value); - }); + }); } }; } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java index 5683c9688..7d688f469 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.logging.internal; import org.apache.logging.log4j.core.LogEvent; @@ -14,7 +28,8 @@ public final class PowertoolsResolverFactory implements EventResolverFactory { private static final PowertoolsResolverFactory INSTANCE = new PowertoolsResolverFactory(); - private PowertoolsResolverFactory() {} + private PowertoolsResolverFactory() { + } @PluginFactory public static PowertoolsResolverFactory getInstance() { 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 index 60f0806a9..47b495da3 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * 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 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; - import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -33,13 +33,13 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Map; - -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 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 { @@ -74,22 +74,24 @@ void shouldLogInStructuredFormat() throws IOException { } @Test - void shouldModifyLogLevelBasedOnEnvVariable() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { + 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"); - }); + .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 @@ -100,22 +102,24 @@ void shouldModifyLogLevelBasedOnSamplingRule() throws IOException { 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"); - }); + .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 { + private void resetLogLevel(Level level) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); resetLogLevels.setAccessible(true); resetLogLevels.invoke(null, level); 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 index eee8ace05..8889fb93c 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,16 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; +import org.apache.logging.log4j.ThreadContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class LoggingUtilsTest { 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/PowerLogToolApiGatewayHttpApiCorrelationId.java index 4e40e0f97..54d87d5cb 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/PowerLogToolApiGatewayHttpApiCorrelationId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.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; @@ -20,8 +23,6 @@ import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_HTTP; - public class PowerLogToolApiGatewayHttpApiCorrelationId implements RequestHandler<APIGatewayV2HTTPEvent, Object> { private final Logger LOG = LogManager.getLogger(PowerLogToolApiGatewayHttpApiCorrelationId.class); 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/PowerLogToolApiGatewayRestApiCorrelationId.java index e3cadaf84..2b6e5a8d4 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/PowerLogToolApiGatewayRestApiCorrelationId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.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; @@ -20,8 +23,6 @@ import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_REST; - public class PowerLogToolApiGatewayRestApiCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, Object> { private final Logger LOG = LogManager.getLogger(PowerLogToolApiGatewayRestApiCorrelationId.class); 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/PowerLogToolEnabled.java index e154bbcf3..df68ea14f 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/PowerLogToolEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; 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/PowerLogToolEnabledForStream.java index e2c2d66d0..83a370437 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/PowerLogToolEnabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,14 @@ * 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 software.amazon.lambda.powertools.logging.Logging; - import java.io.InputStream; import java.io.OutputStream; +import software.amazon.lambda.powertools.logging.Logging; public class PowerLogToolEnabledForStream implements RequestStreamHandler { 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/PowerLogToolSamplingEnabled.java index 9d3d68e2e..357520395 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/PowerLogToolSamplingEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; 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/PowerToolDisabled.java index 0391a5177..48a2e3b81 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/PowerToolDisabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.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/PowerToolDisabledForStream.java index 15b39c6c5..7f93145c7 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/PowerToolDisabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,11 @@ * 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.InputStream; import java.io.OutputStream; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java index 152eb284d..8a960fa87 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java index 473042e6c..9de76586f 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,17 +11,17 @@ * 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 com.fasterxml.jackson.databind.ObjectMapper; -import software.amazon.lambda.powertools.logging.Logging; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; +import software.amazon.lambda.powertools.logging.Logging; public class PowerToolLogEventEnabledForStream implements RequestStreamHandler { 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 index f1c2f62c8..838de1216 100644 --- 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -8,11 +22,10 @@ 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; -import java.io.IOException; - public class PowerToolLogEventEnabledWithCustomMapper implements RequestHandler<S3EventNotification, Object> { static { @@ -40,7 +53,8 @@ public S3EventNotificationSerializer(Class<S3EventNotification> t) { } @Override - public void serialize(S3EventNotification o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + 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 c06f8326e..a32e3e06e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.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; @@ -20,8 +23,6 @@ import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.APPLICATION_LOAD_BALANCER; - public class PowertoolsLogAlbCorrelationId implements RequestHandler<ApplicationLoadBalancerRequestEvent, Object> { private final Logger LOG = LogManager.getLogger(PowertoolsLogAlbCorrelationId.class); 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/PowertoolsLogEnabledWithClearState.java index 8b7d4fcaa..f21d9f118 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/PowertoolsLogEnabledWithClearState.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -21,8 +22,8 @@ import software.amazon.lambda.powertools.logging.LoggingUtils; public class PowertoolsLogEnabledWithClearState implements RequestHandler<Object, Object> { - public static int COUNT = 1; private static final Logger LOG = LogManager.getLogger(PowertoolsLogEnabledWithClearState.class); + public static int COUNT = 1; @Override @Logging(clearState = true) 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 f03983aff..53e06cb2e 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 @@ -11,19 +11,19 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.EVENT_BRIDGE; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.logging.Logging; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; - -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.EVENT_BRIDGE; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; public class PowertoolsLogEventBridgeCorrelationId implements RequestStreamHandler { 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 8e7d2d3e6..b78710586 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,21 @@ * limitations under the License. * */ + 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 com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -23,6 +36,21 @@ 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.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.Paths; +import java.nio.file.StandardOpenOption; +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; @@ -46,34 +74,6 @@ import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledWithClearState; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -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.Paths; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -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; - class LambdaLoggingAspectTest { private static final int EXPECTED_CONTEXT_SIZE = 8; @@ -115,7 +115,8 @@ void shouldSetLambdaContextWhenEnabled() { void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { requestStreamHandler = new PowerLogToolEnabledForStream(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) @@ -130,13 +131,15 @@ void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { @Test void shouldSetColdStartFlag() throws IOException { - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) .containsEntry("coldStart", "true"); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) @@ -184,7 +187,8 @@ void shouldLogEventForHandler() throws IOException, JSONException { String event = (String) log.get("message"); - String expectEvent = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) .lines().collect(joining("\n")); assertEquals(expectEvent, event, false); @@ -201,7 +205,8 @@ void shouldLogEventForHandlerWithOverriddenObjectMapper() throws IOException, JS String event = (String) log.get("message"); - String expectEvent = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/customizedLogEvent.json"))) + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/customizedLogEvent.json"))) .lines().collect(joining("\n")); assertEquals(expectEvent, event, false); @@ -213,7 +218,8 @@ void shouldLogEventForStreamAndLambdaStreamIsValid() throws IOException, JSONExc ByteArrayOutputStream output = new ByteArrayOutputStream(); S3EventNotification s3EventNotification = s3EventNotification(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); + requestStreamHandler.handleRequest( + new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isNotEmpty(); @@ -222,7 +228,8 @@ void shouldLogEventForStreamAndLambdaStreamIsValid() throws IOException, JSONExc String event = (String) log.get("message"); - String expectEvent = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) .lines().collect(joining("\n")); assertEquals(expectEvent, event, false); @@ -243,7 +250,8 @@ void shouldLogxRayTraceIdEnvVarSet() { String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); requestHandler.handleRequest(new Object(), context); @@ -328,7 +336,8 @@ private void setupContext() { when(context.getAwsRequestId()).thenReturn("RequestId"); } - private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + private void resetLogLevel(Level level) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); resetLogLevels.setAccessible(true); resetLogLevels.invoke(null, level); @@ -336,25 +345,27 @@ private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAcc } 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") - ); + 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)); } diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index ed2d2f815..7b2e99045 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -112,4 +126,12 @@ </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-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java b/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java index f59658e9c..e2d886fe5 100644 --- 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 @@ -1,9 +1,23 @@ -package software.amazon.cloudwatchlogs.emf.model; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import java.lang.reflect.Field; +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() { } 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 2a4f4d472..fb92c900d 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.metrics; import java.lang.annotation.ElementType; @@ -44,7 +58,10 @@ @Target(ElementType.METHOD) public @interface Metrics { String namespace() default ""; + String service() default ""; + boolean captureColdStart() default false; + boolean raiseOnEmptyMetrics() default false; } 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 index 8705c2da4..1da100f26 100644 --- 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 @@ -1,9 +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.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; @@ -12,16 +31,10 @@ import software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper; import software.amazon.cloudwatchlogs.emf.model.Unit; -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; - /** * A class used to retrieve the instance of the {@code MetricsLogger} used by * {@code Metrics}. - * + * <p> * {@see Metrics} */ public final class MetricsUtils { @@ -43,6 +56,7 @@ public static MetricsLogger 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) { @@ -52,15 +66,15 @@ public static void defaultDimensions(final DimensionSet... 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) { + if (dimensionSet.getDimensionKeys().size() > 0) { defaultDimensions(dimensionSet); } } @@ -81,10 +95,11 @@ 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); - }); + withMetricsLogger(metricsLogger -> + { + metricsLogger.putMetric(name, value, unit); + logger.accept(metricsLogger); + }); } /** @@ -92,22 +107,23 @@ public static void withSingleMetric(final String name, * 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 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 + * @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); - }); + withMetricsLogger(metricsLogger -> + { + metricsLogger.setNamespace(namespace); + metricsLogger.putMetric(name, value, unit); + logger.accept(metricsLogger); + }); } /** @@ -137,7 +153,6 @@ public static void withMetricsLogger(final Consumer<MetricsLogger> logger) { * capture xray_trace_id as property if tracing is enabled. * * @param logger the MetricsLogger - * * @deprecated use {@link MetricsUtils#withMetricsLogger} instead */ @Deprecated @@ -164,7 +179,7 @@ private static void captureRequestAndTraceId(MetricsLogger metricsLogger) { private static String defaultNameSpace() { MetricsContext context = MetricsLoggerHelper.metricsContext(); return "aws-embedded-metrics".equals(context.getNamespace()) ? - SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE"): context.getNamespace(); + SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE") : context.getNamespace(); } private static Optional<String> awsRequestId() { diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java index 2da9a539c..a553abbbd 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.metrics; public class ValidationException extends RuntimeException { 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 927359fc5..8ca069b01 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 @@ -1,8 +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.internal; -import java.lang.reflect.Field; +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 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; @@ -16,21 +39,33 @@ import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; -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; - @Aspect public class LambdaMetricsAspect { - private static final String NAMESPACE = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); 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(); + } + + // 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(); + + DimensionSet[] defaultDimensions = hasDefaultDimension() ? MetricsUtils.getDefaultDimensions() + : new DimensionSet[] {DimensionSet.of("Service", service(metrics))}; + + context.setDimensions(defaultDimensions); + + f.set(metricsLogger(), context); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } @SuppressWarnings({"EmptyMethod"}) @Pointcut("@annotation(metrics)") @@ -52,8 +87,9 @@ public Object around(ProceedingJoinPoint pjp, Context extractedContext = extractContext(pjp); - if( null != extractedContext) { - coldStartSingleMetricIfApplicable(extractedContext.getAwsRequestId(), extractedContext.getFunctionName(), metrics); + if (null != extractedContext) { + coldStartSingleMetricIfApplicable(extractedContext.getAwsRequestId(), + extractedContext.getFunctionName(), metrics); logger.putProperty(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); } @@ -79,12 +115,12 @@ private void coldStartSingleMetricIfApplicable(final String awsRequestId, 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(); + 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(); } } @@ -104,34 +140,12 @@ private String namespace(Metrics metrics) { return !"".equals(metrics.namespace()) ? metrics.namespace() : NAMESPACE; } - private static String service(Metrics metrics) { - return !"".equals(metrics.service()) ? metrics.service() : serviceName(); - } - private void validateMetricsAndRefreshOnFailure(Metrics metrics) { try { validateBeforeFlushingMetrics(metrics); - } catch (ValidationException e){ + } catch (ValidationException e) { refreshMetricsContext(metrics); throw e; } } - - // 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(); - - DimensionSet[] defaultDimensions = hasDefaultDimension() ? MetricsUtils.getDefaultDimensions() - : new DimensionSet[]{DimensionSet.of("Service", service(metrics))}; - - context.setDimensions(defaultDimensions); - - f.set(metricsLogger(), context); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } } 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 index 6ebf30e04..da4162ea0 100644 --- 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 @@ -1,12 +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; +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 com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -17,18 +36,19 @@ import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; -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; - 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)); @@ -39,88 +59,92 @@ void tearDown() { System.setOut(originalOut); } - @BeforeAll - static void beforeAll() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - } - } - @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)) { + 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\""); + 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 -> {}); + 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"); - }); + .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)) { + 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\""); + 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"); - }); + .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)) { + 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\""); + 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"); - }); + .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"); + }); } } @@ -143,32 +167,36 @@ void shouldThrowExceptionWhenDefaultDimensionIsNull() { 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)) { + 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\""); + 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); - }); + 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"); - }); + .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"); + }); } } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java index a722bd689..e3a0fa22e 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java @@ -1,13 +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.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.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsColdStartEnabledHandler implements RequestHandler<Object, Object> { @Override 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 index f66269546..5d7fb7120 100644 --- 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 @@ -1,5 +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.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; @@ -7,10 +25,6 @@ import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -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; - public class PowertoolsMetricsEnabledDefaultDimensionHandler implements RequestHandler<Object, Object> { static { @@ -23,7 +37,9 @@ public Object handleRequest(Object input, Context context) { MetricsLogger metricsLogger = metricsLogger(); metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - withSingleMetric("Metric2", 1, Unit.COUNT, log -> {}); + 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 index 761362f43..0a0079b80 100644 --- 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 @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; @@ -7,9 +24,6 @@ import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.metrics.MetricsUtils; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - public class PowertoolsMetricsEnabledDefaultNoDimensionHandler implements RequestHandler<Object, Object> { static { @@ -22,7 +36,9 @@ public Object handleRequest(Object input, Context context) { MetricsLogger metricsLogger = metricsLogger(); metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - withSingleMetric("Metric2", 1, Unit.COUNT, log -> {}); + 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 index 160109787..7cfee533d 100644 --- 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 @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; @@ -7,9 +24,6 @@ import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - public class PowertoolsMetricsEnabledHandler implements RequestHandler<Object, Object> { @Override 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 index 2eb877dc3..1600f4a64 100644 --- 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 @@ -1,16 +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.metrics.handlers; -import java.io.InputStream; -import java.io.OutputStream; +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; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsEnabledStreamHandler implements RequestStreamHandler { @Override 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 index 8ada044ee..42e0b3ad4 100644 --- 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 @@ -1,12 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsExceptionWhenNoMetricsHandler implements RequestHandler<Object, Object> { @Override 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 index 1c4cc3f77..04b02e166 100644 --- 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 @@ -1,13 +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.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; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsNoDimensionsHandler implements RequestHandler<Object, Object> { @Override 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 index 3639542f8..c08ce2f86 100644 --- 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 @@ -1,12 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsNoExceptionWhenNoMetricsHandler implements RequestHandler<Object, Object> { @Override 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 index ccec863f9..bc8a6e949 100644 --- 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 @@ -1,15 +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.metrics.handlers; -import java.util.stream.IntStream; +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; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsTooManyDimensionsHandler implements RequestHandler<Object, Object> { @Override 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 index db75d9f95..da9028a70 100644 --- 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 @@ -1,12 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsWithExceptionInHandler implements RequestHandler<Object, Object> { @Override 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 6c18d5d7a..44202b8b8 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 @@ -1,16 +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.metrics.internal; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.Map; +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.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -18,7 +40,6 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; @@ -33,22 +54,12 @@ import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsTooManyDimensionsHandler; import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsWithExceptionInHandler; -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; - public class LambdaMetricsAspectTest { - @Mock - private Context context; - 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; @@ -75,10 +86,12 @@ void tearDown() { @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)) { + 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\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); MetricsUtils.defaultDimensions(null); requestHandler = new PowertoolsMetricsEnabledHandler(); @@ -86,40 +99,43 @@ public void metricsWithoutColdStart() { 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"); - }); + .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"); + }); } } @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)) { + 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\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); requestHandler = new PowertoolsMetricsEnabledDefaultDimensionHandler(); @@ -127,40 +143,43 @@ public void metricsWithDefaultDimensionSpecified() { 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"); - }); + .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"); + }); } } @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)) { + 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\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); requestHandler = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); @@ -168,28 +187,29 @@ public void metricsWithDefaultNoDimensionSpecified() { assertThat(out.toString().split("\n")) .hasSize(2) - .satisfies(s -> { - Map<String, Object> logAsJson = readAsJson(s[0]); + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s[0]); - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); + assertThat(logAsJson) + .containsEntry("Metric2", 1.0) + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") + .containsEntry("function_request_id", "123ABC"); - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); - logAsJson = readAsJson(s[1]); + logAsJson = readAsJson(s[1]); - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -207,25 +227,26 @@ public void metricsWithColdStart() { assertThat(out.toString().split("\n")) .hasSize(2) - .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"); - }); + .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"); + }); } } @@ -243,34 +264,35 @@ public void noColdStartMetricsWhenColdStartDone() { 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"); - }); + .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"); + }); } } @@ -283,18 +305,19 @@ public void metricsWithStreamHandler() throws IOException { MetricsUtils.defaultDimensions(null); RequestStreamHandler streamHandler = new PowertoolsMetricsEnabledStreamHandler(); - streamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + streamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); assertThat(out.toString()) - .satisfies(s -> { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -324,13 +347,14 @@ public void noExceptionWhenNoMetricsEmitted() { requestHandler.handleRequest("input", context); assertThat(out.toString()) - .satisfies(s -> { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Service", "booking") - .doesNotContainKey("_aws"); - }); + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Service", "booking") + .doesNotContainKey("_aws"); + }); } } @@ -344,13 +368,14 @@ public void allowWhenNoDimensionsSet() { 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"); - }); + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + assertThat(logAsJson) + .containsEntry("CoolMetric", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -383,14 +408,15 @@ public void metricsPublishedEvenHandlerThrowsException() { .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"); - }); + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + assertThat(logAsJson) + .containsEntry("CoolMetric", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index b50844d99..fab72a9b7 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -144,7 +158,10 @@ </environmentVariables> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + </plugin> </plugins> </build> - </project> 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 index c62d7a2e5..130be25a3 100644 --- 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 @@ -1,5 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -10,15 +26,12 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.util.HashMap; -import java.util.Map; - /** * 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 @@ -27,24 +40,11 @@ * @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 static class EstablishedSession { - private final String nextSessionToken; - private final String lastConfigurationValue; - - private EstablishedSession(String nextSessionToken, String value) { - this.nextSessionToken = nextSessionToken; - this.lastConfigurationValue = value; - } - } +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) { @@ -54,6 +54,14 @@ private EstablishedSession(String nextSessionToken, String value) { 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 /> @@ -67,18 +75,18 @@ protected String getValue(String key) { // 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? + String sessionToken = establishedSession != null ? establishedSession.nextSessionToken : client.startConfigurationSession(StartConfigurationSessionRequest.builder() - .applicationIdentifier(this.application) - .environmentIdentifier(this.environment) - .configurationProfileIdentifier(key) - .build()) - .initialConfigurationToken(); + .applicationIdentifier(this.application) + .environmentIdentifier(this.environment) + .configurationProfileIdentifier(key) + .build()) + .initialConfigurationToken(); // Get the configuration using the token GetLatestConfigurationResponse response = client.getLatestConfiguration(GetLatestConfigurationRequest.builder() - .configurationToken(sessionToken) + .configurationToken(sessionToken) .build()); // Get the next session token we'll use next time we are asked for this key @@ -87,11 +95,12 @@ protected String getValue(String key) { // 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? + 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 + 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)); @@ -102,16 +111,18 @@ protected String getValue(String key) { @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"); + throw new RuntimeException( + "Retrieving multiple parameter values is not supported with the AWS App Config Provider"); } - /** - * 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(); + 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 { 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 fb539f850..e7bfdf835 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Map; import software.amazon.awssdk.annotations.NotThreadSafe; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.TransformationException; @@ -20,12 +26,6 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Map; - /** * Base class for all parameter providers. */ @@ -106,7 +106,8 @@ public BaseProvider withMaxAge(int maxAge, ChronoUnit unit) { */ public BaseProvider withTransformation(Class<? extends Transformer> transformerClass) { if (transformationManager == null) { - throw new IllegalStateException("Trying to add transformation while no TransformationManager has been provided."); + throw new IllegalStateException( + "Trying to add transformation while no TransformationManager has been provided."); } transformationManager.setTransformer(transformerClass); return this; @@ -126,15 +127,16 @@ public Map<String, String> getMultiple(String path) { // remove trailing whitespace String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { - return (Map<String, String>) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> { - Map<String, String> params = getMultipleValues(pathWithoutTrailingSlash); + return (Map<String, String>) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> + { + Map<String, String> params = getMultipleValues(pathWithoutTrailingSlash); - cacheManager.putInCache(pathWithoutTrailingSlash, params); + cacheManager.putInCache(pathWithoutTrailingSlash, params); - params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); + params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); - return params; - }); + return params; + }); } finally { resetToDefaults(); } @@ -148,24 +150,25 @@ public Map<String, String> getMultiple(String path) { * * @param key key of the parameter * @return the String value of the parameter - * @throws IllegalStateException if a wrong transformer class is provided through {@link #withTransformation(Class)}. Needs to be a {@link BasicTransformer}. - * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. + * @throws IllegalStateException if a wrong transformer class is provided through {@link #withTransformation(Class)}. Needs to be a {@link BasicTransformer}. + * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override public String get(final String key) { try { - return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { - String value = getValue(key); + return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> + { + String value = getValue(key); - String transformedValue = value; - if (transformationManager != null && transformationManager.shouldTransform()) { - transformedValue = transformationManager.performBasicTransformation(value); - } + String transformedValue = value; + if (transformationManager != null && transformationManager.shouldTransform()) { + transformedValue = transformationManager.performBasicTransformation(value); + } - cacheManager.putInCache(key, transformedValue); + cacheManager.putInCache(key, transformedValue); - return transformedValue; - }); + return transformedValue; + }); } finally { // in all case, we reset options to default, for next call resetToDefaults(); @@ -181,24 +184,26 @@ public String get(final String key) { * @param key key of the parameter * @param targetClass class of the target Object (after transformation) * @return the Object (T) value of the parameter - * @throws IllegalStateException if no transformation class was provided through {@link #withTransformation(Class)} - * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. + * @throws IllegalStateException if no transformation class was provided through {@link #withTransformation(Class)} + * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override public <T> T get(final String key, final Class<T> targetClass) { try { - return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { - String value = getValue(key); + return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> + { + String value = getValue(key); - if (transformationManager == null) { - throw new IllegalStateException("Trying to transform value while no TransformationManager has been provided."); - } - T transformedValue = transformationManager.performComplexTransformation(value, targetClass); + if (transformationManager == null) { + throw new IllegalStateException( + "Trying to transform value while no TransformationManager has been provided."); + } + T transformedValue = transformationManager.performComplexTransformation(value, targetClass); - cacheManager.putInCache(key, transformedValue); + cacheManager.putInCache(key, transformedValue); - return transformedValue; - }); + return transformedValue; + }); } finally { // in all case, we reset options to default, for next call resetToDefaults(); @@ -225,6 +230,7 @@ protected void setTransformationManager(TransformationManager transformationMana /** * For test purpose + * * @param clock */ void setClock(Clock clock) { 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 index e09f23348..2b0694a5d 100644 --- 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 @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -13,16 +30,11 @@ import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.util.Collections; -import java.util.Map; -import java.util.stream.Collectors; - /** * 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 { @@ -39,6 +51,15 @@ public class DynamoDbProvider extends BaseProvider { 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. * @@ -82,9 +103,10 @@ protected Map<String, String> getMultipleValues(String path) { .build()); return resp - .items() - .stream() - .peek((i) -> { + .items() + .stream() + .peek((i) -> + { if (!i.containsKey("sk")) { throw new DynamoDbProviderSchemaException("Missing 'sk': " + i.toString()); } @@ -92,29 +114,27 @@ protected Map<String, String> getMultipleValues(String path) { throw new DynamoDbProviderSchemaException("Missing 'value': " + i.toString()); } }) - .collect( - Collectors.toMap( - (i) -> i.get("sk").s(), - (i) -> i.get("value").s())); + .collect( + Collectors.toMap( + (i) -> i.get("sk").s(), + (i) -> i.get("value").s())); } - /** - * 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(); - } - 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()))) + .build(); + } + /** * Create a {@link DynamoDbProvider} instance. * @@ -183,12 +203,5 @@ public DynamoDbProvider.Builder withTransformationManager(TransformationManager this.transformationManager = transformationManager; return this; } - - private static DynamoDbClient createClient() { - return DynamoDbClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(); - } } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java index ef3d08b72..7ffb0310c 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java @@ -1,11 +1,24 @@ -package software.amazon.lambda.powertools.parameters; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import software.amazon.lambda.powertools.parameters.transform.Transformer; +package software.amazon.lambda.powertools.parameters; 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; /** * {@code Param} is used to signal that the annotated field should be @@ -25,6 +38,8 @@ @Target(ElementType.FIELD) public @interface Param { String key(); + Class<? extends BaseProvider> provider() default SSMProvider.class; + Class<? extends Transformer> transformer() default Transformer.class; } 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 index c8abedf06..6fee0f114 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * 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; @@ -20,9 +23,6 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.lang.reflect.Constructor; -import java.util.concurrent.ConcurrentHashMap; - /** * Utility class to retrieve instances of parameter providers. * Each instance is unique (singleton). @@ -39,8 +39,9 @@ public final class ParamManager { * 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. - * @deprecated You should not use this method directly but a typed one (getSecretsProvider, getSsmProvider, getDynamoDbProvider, getAppConfigProvider), will be removed in v2 + * * @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) { @@ -48,7 +49,8 @@ public static <T extends BaseProvider> T getProvider(Class<T> providerClass) { 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"); + throw new IllegalArgumentException( + providerClass + " cannot be instantiated like this, additional parameters are required"); } return (T) providers.computeIfAbsent(providerClass, ParamManager::createProvider); } @@ -56,6 +58,7 @@ public static <T extends BaseProvider> T getProvider(Class<T> providerClass) { /** * 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() { @@ -65,6 +68,7 @@ public static SecretsProvider getSecretsProvider() { /** * 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() { @@ -89,6 +93,7 @@ public static DynamoDbProvider getDynamoDbProvider(String tableName) { /** * 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) { @@ -107,6 +112,7 @@ public static AppConfigProvider getAppConfigProvider(String environment, String /** * 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) { @@ -120,6 +126,7 @@ public static SecretsProvider getSecretsProvider(SecretsManagerClient client) { /** * 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) { @@ -133,6 +140,7 @@ public static SSMProvider getSsmProvider(SsmClient client) { /** * 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) { @@ -143,13 +151,15 @@ public static DynamoDbProvider getDynamoDbProvider(DynamoDbClient client, String .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) { + public static AppConfigProvider getAppConfigProvider(AppConfigDataClient client, String environment, + String application) { return (AppConfigProvider) providers.computeIfAbsent(AppConfigProvider.class, (k) -> AppConfigProvider.builder() .withClient(client) .withCacheManager(cacheManager) @@ -168,10 +178,11 @@ public static TransformationManager getTransformationManager() { return transformationManager; } - static <T extends BaseProvider> T createProvider(Class<T> providerClass) { + 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) + T provider = + constructor.newInstance(cacheManager); // FIXME: avoid reflection here as we may have issues (#1280) provider.setTransformationManager(transformationManager); return provider; } catch (ReflectiveOperationException e) { diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java index b496ed4f3..ba4232261 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; import java.util.Map; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index 1fa4dbaab..b24b1e319 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,12 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -25,10 +29,6 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.Map; - /** * AWS System Manager Parameter Store Provider <br/><br/> * @@ -88,14 +88,24 @@ public class SSMProvider extends BaseProvider { /** * Constructor with only a CacheManager<br/> - * + * <p> * Used in {@link ParamManager#createProvider(Class)} + * * @param cacheManager handles the parameter caching */ SSMProvider(CacheManager cacheManager) { this(cacheManager, Builder.createClient()); } + /** + * Create a builder that can be used to configure and create a {@link SSMProvider}. + * + * @return a new instance of {@link SSMProvider.Builder} + */ + public static SSMProvider.Builder builder() { + return new SSMProvider.Builder(); + } + /** * Retrieve the parameter value from the AWS System Manager Parameter Store. * @@ -194,19 +204,20 @@ 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 -> { + 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. */ - String name = parameter.name(); - if (name.startsWith(path)) { - name = name.replaceFirst(path, ""); - } - name = name.replaceFirst("/", ""); - params.put(name, parameter.value()); - }); + String name = parameter.name(); + if (name.startsWith(path)) { + name = name.replaceFirst(path, ""); + } + name = name.replaceFirst("/", ""); + params.put(name, parameter.value()); + }); } if (!StringUtils.isEmpty(res.nextToken())) { @@ -228,21 +239,18 @@ SsmClient getClient() { return client; } - /** - * Create a builder that can be used to configure and create a {@link SSMProvider}. - * - * @return a new instance of {@link SSMProvider.Builder} - */ - public static SSMProvider.Builder builder() { - return new SSMProvider.Builder(); - } - - 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()))) + .build(); + } + /** * Create a {@link SSMProvider} instance. * @@ -277,13 +285,6 @@ public SSMProvider.Builder withClient(SsmClient client) { return this; } - private static SsmClient createClient() { - return SsmClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(); - } - /** * <b>Mandatory</b>. Provide a CacheManager to the {@link SSMProvider} * 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 index fd45da881..99b87f84b 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,14 @@ * 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.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -22,12 +28,6 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.temporal.ChronoUnit; -import java.util.Base64; -import java.util.Map; - -import static java.nio.charset.StandardCharsets.UTF_8; - /** * AWS Secrets Manager Parameter Provider<br/><br/> * @@ -59,7 +59,7 @@ public class SecretsProvider extends BaseProvider { /** * 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. @@ -71,14 +71,24 @@ public class SecretsProvider extends BaseProvider { /** * 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. * @@ -91,13 +101,14 @@ protected String getValue(String key) { String secretValue = client.getSecretValue(request).secretString(); if (secretValue == null) { - secretValue = new String(Base64.getDecoder().decode(client.getSecretValue(request).secretBinary().asByteArray()), UTF_8); + 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 @@ -137,21 +148,19 @@ SecretsManagerClient getClient() { return client; } - /** - * 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(); - } - 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()))) + .build(); + } + /** * Create a {@link SecretsProvider} instance. * @@ -186,13 +195,6 @@ public Builder withClient(SecretsManagerClient client) { return this; } - private static SecretsManagerClient createClient() { - return SecretsManagerClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(); - } - /** * <b>Mandatory</b>. Provide a CacheManager to the {@link SecretsProvider} * 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 687337a96..b868cb642 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.cache; +import static java.time.temporal.ChronoUnit.SECONDS; + import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.Optional; -import static java.time.temporal.ChronoUnit.SECONDS; - public class CacheManager { static final Duration DEFAULT_MAX_AGE_SECS = Duration.of(5, SECONDS); 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 9ad8df12c..737faa353 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.cache; import java.time.Instant; @@ -27,21 +28,11 @@ public DataStore() { this.store = new ConcurrentHashMap<>(); } - static class ValueNode { - public final Object value; - public final Instant time; - - public ValueNode(Object value, Instant time){ - this.value = value; - this.time = time; - } - } - - public void put(String key, Object value, Instant time){ + public void put(String key, Object value, Instant time) { store.put(key, new ValueNode(value, time)); } - public void remove(String Key){ + public void remove(String Key) { store.remove(Key); } @@ -51,11 +42,21 @@ public Object get(String key) { } public boolean hasExpired(String key, Instant now) { - boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); + boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); // Auto-clean if the parameter has expired if (hasExpired) { remove(key); } return hasExpired; } + + static class ValueNode { + public final Object value; + public final Instant time; + + public ValueNode(Object value, Instant time) { + this.value = value; + this.time = time; + } + } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java index b7574e81d..77df6e3d3 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.parameters.exception; /** diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java index 7d28d12d1..f071c8a6b 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.exception; public class TransformationException extends RuntimeException { @@ -19,6 +20,7 @@ public TransformationException(Exception e) { super(e); } - public TransformationException(String message) { super(message); + public TransformationException(String message) { + super(message); } } 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 index 8de2f3f57..081af108d 100644 --- 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.parameters.internal; import org.aspectj.lang.ProceedingJoinPoint; @@ -20,12 +34,12 @@ public void getParam(Param paramAnnotation) { public Object injectParam(final ProceedingJoinPoint joinPoint, final Param paramAnnotation) { BaseProvider provider = ParamManager.getProvider(paramAnnotation.provider()); - if(paramAnnotation.transformer().isInterface()) { + if (paramAnnotation.transformer().isInterface()) { // No transformation return provider.get(paramAnnotation.key()); } else { FieldSignature s = (FieldSignature) joinPoint.getSignature(); - if(String.class.isAssignableFrom(s.getFieldType())) { + if (String.class.isAssignableFrom(s.getFieldType())) { // Basic transformation return provider .withTransformation(paramAnnotation.transformer()) diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java index c666edce7..e8557ebfd 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; +import static java.nio.charset.StandardCharsets.UTF_8; import java.util.Base64; - -import static java.nio.charset.StandardCharsets.UTF_8; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; /** * Transformer that take a base64 encoded string and return a decoded string. diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java index 5251d9f16..92e73d9b0 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; import software.amazon.lambda.powertools.parameters.exception.TransformationException; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java index d84a1ab3a..0eff58ea8 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; import com.fasterxml.jackson.core.JsonProcessingException; 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 00e6f84a9..d3fbce14f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,11 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; +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/> @@ -50,15 +50,18 @@ public boolean shouldTransform() { */ public String performBasicTransformation(String value) { if (transformer == null) { - throw new IllegalStateException("You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); + throw new IllegalStateException( + "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } if (!BasicTransformer.class.isAssignableFrom(transformer)) { throw new IllegalStateException("Wrong Transformer for a String, choose a BasicTransformer."); } try { - BasicTransformer basicTransformer = (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); + BasicTransformer basicTransformer = + (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); return basicTransformer.applyTransformation(value); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException e) { throw new TransformationException(e); } } @@ -66,19 +69,21 @@ public String performBasicTransformation(String value) { /** * Transform a String in a Java Object. * - * @param value the value to transform + * @param value the value to transform * @param targetClass the type of the target object. * @return the value transformed in an object ot type T. */ public <T> T performComplexTransformation(String value, Class<T> targetClass) { if (transformer == null) { - throw new IllegalStateException("You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); + 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); 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/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java index 3c57b2aa9..d9aea2644 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; import software.amazon.lambda.powertools.parameters.exception.TransformationException; @@ -34,7 +35,8 @@ public interface Transformer<T> { /** * Apply a transformation on the input value (String) - * @param value the parameter value to transform + * + * @param value the parameter value to transform * @param targetClass class of the target object * @return a transformed parameter * @throws TransformationException when a transformation error occurs diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java index 23f6271da..f467dca72 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java @@ -1,5 +1,24 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; +import static org.mockito.MockitoAnnotations.openMocks; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -15,23 +34,18 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -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; - public class AppConfigProviderTest { private final String environmentName = "test"; private final String applicationName = "fakeApp"; private final String defaultTestKey = "key1"; - + @Mock AppConfigDataClient client; - + @Captor ArgumentCaptor<StartConfigurationSessionRequest> startSessionRequestCaptor; - + @Captor ArgumentCaptor<GetLatestConfigurationRequest> getLatestConfigurationRequestCaptor; private AppConfigProvider provider; @@ -89,13 +103,17 @@ public void getValueRetrievesValue() { // 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(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(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo(firstSession.initialConfigurationToken()); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo(firstResponse.nextPollConfigurationToken()); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(2).configurationToken()).isEqualTo(secondResponse.nextPollConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo( + firstSession.initialConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo( + firstResponse.nextPollConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(2).configurationToken()).isEqualTo( + secondResponse.nextPollConfigurationToken()); } @Test @@ -156,10 +174,14 @@ public void multipleKeysRetrievalWorks() { // Assert assertThat(firstKeyValue).isEqualTo(param1Response.configuration().asUtf8String()); assertThat(secondKeyValue).isEqualTo(param2Response.configuration().asUtf8String()); - assertThat(startSessionRequestCaptor.getAllValues().get(0).configurationProfileIdentifier()).isEqualTo(param1Key); - assertThat(startSessionRequestCaptor.getAllValues().get(1).configurationProfileIdentifier()).isEqualTo(param2Key); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo(param1Session.initialConfigurationToken()); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo(param2Session.initialConfigurationToken()); + assertThat(startSessionRequestCaptor.getAllValues().get(0).configurationProfileIdentifier()).isEqualTo( + param1Key); + assertThat(startSessionRequestCaptor.getAllValues().get(1).configurationProfileIdentifier()).isEqualTo( + param2Key); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo( + param1Session.initialConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo( + param2Session.initialConfigurationToken()); } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index 8dd2d7658..edc671e2c 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,8 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters; - -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; -import java.time.Clock; -import java.time.temporal.ChronoUnit; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; +package software.amazon.lambda.powertools.parameters; import static java.time.Clock.offset; import static java.time.Duration.of; @@ -36,6 +24,18 @@ 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.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 { Clock clock; @@ -45,33 +45,6 @@ public class BaseProviderTest { boolean getFromStore = false; - class BasicProvider extends BaseProvider { - - public BasicProvider(CacheManager cacheManager) { - super(cacheManager); - } - - private String value = "valueFromStore"; - - public void setValue(String value) { - this.value = value; - } - - @Override - protected String getValue(String key) { - getFromStore = true; - return value; - } - - @Override - protected Map<String, String> getMultipleValues(String path) { - getFromStore = true; - Map<String, String> map = new HashMap<>(); - map.put(path, value); - return map; - } - } - @BeforeEach public void setup() { openMocks(this); @@ -224,10 +197,11 @@ public void get_basicTransformation_shouldTransformInString() { public 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 -> o.getFoo().equals("Foo") && o.getBar() == 42 && o.getBaz() == 123456789); } @@ -400,4 +374,31 @@ public void getTwoParams_shouldResetTransformationOptionsInBetween() { assertThat(foob64).isEqualTo("base64encoded"); assertThat(foostr).isEqualTo("string"); } + + class BasicProvider extends BaseProvider { + + private String value = "valueFromStore"; + + public BasicProvider(CacheManager cacheManager) { + super(cacheManager); + } + + public void setValue(String value) { + this.value = value; + } + + @Override + protected String getValue(String key) { + getFromStore = true; + return value; + } + + @Override + protected Map<String, String> getMultipleValues(String path) { + getFromStore = true; + Map<String, String> map = new HashMap<>(); + map.put(path, value); + return map; + } + } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java index c9397676b..18212b45c 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java @@ -1,5 +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.parameters; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -8,16 +26,11 @@ import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - /** * This class provides simple end-to-end style testing of the DynamoDBProvider class. * It is ignored, for now, as it requires AWS access and that's not yet run as part * of our unit test suite in the cloud. - * + * <p> * The test is kept here for 1/ local development and 2/ in preparation for future * E2E tests running in the cloud CI. Once the E2E test structure is merged we * will move this across. @@ -46,8 +59,8 @@ public void TestGetValue() { testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(ParamsTestTable) - .item(testItem) + .tableName(ParamsTestTable) + .item(testItem) .build()); // Act diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java index d6818a64f..abfc9ab8a 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java @@ -1,5 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.MockitoAnnotations.openMocks; + +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; @@ -17,31 +38,18 @@ import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.MockitoAnnotations.openMocks; - public class DynamoDbProviderTest { + private final String tableName = "ddb-test-table"; @Mock DynamoDbClient client; - @Mock TransformationManager transformationManager; - @Captor ArgumentCaptor<GetItemRequest> getItemValueCaptor; - @Captor ArgumentCaptor<QueryRequest> queryRequestCaptor; - - private DynamoDbProvider provider; - private final String tableName = "ddb-test-table"; @BeforeEach public void init() { @@ -114,9 +122,10 @@ public void getValueWithMalformedRowThrows() { .item(responseData) .build()); // Act - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - provider.getValue(key); - }); + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> + { + provider.getValue(key); + }); } @@ -180,10 +189,11 @@ public void getMultipleValuesMissingSortKey_throwsException() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - // Act - provider.getMultipleValues(key); - }); + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> + { + // Act + provider.getMultipleValues(key); + }); } @Test @@ -200,10 +210,11 @@ public void getValuesWithMalformedRowThrows() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - // Act - provider.getMultipleValues(key); - }); + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> + { + // Act + provider.getMultipleValues(key); + }); } @Test @@ -214,6 +225,7 @@ public void testDynamoDBBuilderMissingCacheManager_throwsException() { .withTable("table") .build()); } + @Test public void testDynamoDBBuilderMissingTable_throwsException() { diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java index e1cb72be9..d6fbe66f0 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,21 @@ * limitations under the License. * */ + 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; @@ -31,19 +44,6 @@ import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.services.ssm.model.Parameter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -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; - public class ParamManagerIntegrationTest { @Mock @@ -51,21 +51,16 @@ public class ParamManagerIntegrationTest { @Mock DynamoDbClient ddbClient; - - @Mock - private AppConfigDataClient appConfigDataClient; - @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 { 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 index a21a6082c..b84fcf743 100644 --- 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 @@ -11,18 +11,19 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters; -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; +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 diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java index e55f3d7e6..6a5aa3e68 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,22 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +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 java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import org.assertj.core.data.MapEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,20 +42,6 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -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; - public class SSMProviderTest { @Mock @@ -165,7 +165,8 @@ public 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()); @@ -184,11 +185,12 @@ 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)).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"); diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java index 2ab72ffdd..f4f2d9bee 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +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 java.time.temporal.ChronoUnit; +import java.util.Base64; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -26,14 +34,6 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.time.temporal.ChronoUnit; -import java.util.Base64; - -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; - public class SecretsProviderTest { @Mock @@ -76,7 +76,8 @@ public 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); @@ -99,9 +100,9 @@ public void testSecretsProviderBuilderMissingCacheManager_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> SecretsProvider.builder() - .withClient(client) - .withTransformationManager(transformationManager) - .build()) + .withClient(client) + .withTransformationManager(transformationManager) + .build()) .withMessage("No CacheManager provided, please provide one"); } } 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 index 2464b4278..2bcfcc566 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,19 +11,19 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.cache; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.time.Clock; -import java.util.Optional; +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; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java index c68992bf1..e86ded9be 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,19 +11,19 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.cache; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.time.Clock; -import java.time.Instant; +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.time.Instant; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class DataStoreTest { Clock clock; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java index b58ad7b3d..074a08844 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java @@ -1,11 +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.parameters.internal; public class AnotherObject { - public AnotherObject() {} - private String another; private int object; + public AnotherObject() { + } public String getAnother() { return another; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java index e58ef746c..2c9db3712 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java @@ -1,11 +1,24 @@ -package software.amazon.lambda.powertools.parameters.internal; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import software.amazon.lambda.powertools.parameters.BaseProvider; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; +package software.amazon.lambda.powertools.parameters.internal; import java.util.Base64; import java.util.HashMap; import java.util.Map; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; public class CustomProvider extends BaseProvider { 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 index c390a051e..d346a1aa4 100644 --- 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 @@ -1,5 +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.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; @@ -12,14 +34,6 @@ import software.amazon.lambda.powertools.parameters.transform.JsonTransformer; import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; -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; - public class LambdaParametersAspectTest { @Mock @@ -75,14 +89,17 @@ public void testWithComplexTransform() { .isInstanceOf(ObjectToDeserialize.class) .matches( o -> o.getFoo().equals("Foo") && - o.getBar() == 42 && - o.getBaz() == 123456789); + o.getBar() == 42 && + o.getBaz() == 123456789); } @Test public void testWithComplexTransformWrongTargetClass_ShouldThrowException() { assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> {AnotherObject obj = wrongTransform; }); + .isThrownBy(() -> + { + AnotherObject obj = wrongTransform; + }); } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java index 428b7e0ab..ea713b552 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,16 +11,16 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; - -import java.util.Base64; +package software.amazon.lambda.powertools.parameters.transform; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import java.util.Base64; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; + public class Base64TransformerTest { @Test diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java index fe4fae0bb..5cb980cc7 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,35 +11,38 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; - -import java.util.Map; +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.data.MapEntry.entry; +import java.util.Map; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; + public class JsonTransformerTest { @Test public void transform_json_shouldTransformInObject() throws TransformationException { JsonTransformer<ObjectToDeserialize> transformation = new JsonTransformer<>(); - ObjectToDeserialize objectToDeserialize = transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); + ObjectToDeserialize objectToDeserialize = + transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( o -> o.getFoo().equals("Foo") - && o.getBar() == 42 - && o.getBaz() == 123456789); + && o.getBar() == 42 + && o.getBaz() == 123456789); } @Test public void transform_json_shouldTransformInHashMap() throws TransformationException { JsonTransformer<Map> transformation = new JsonTransformer<>(); - Map<String, Object> map = transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", Map.class); + Map<String, Object> map = + transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", Map.class); assertThat(map).contains( entry("foo", "Foo"), entry("bar", 42), @@ -51,6 +54,7 @@ public void transform_badJson_shouldThrowException() { JsonTransformer<ObjectToDeserialize> transformation = new JsonTransformer<>(); assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> transformation.applyTransformation("{\"fo\":\"Foo\", \"bat\":42, \"bau\":123456789}", ObjectToDeserialize.class)); + .isThrownBy(() -> transformation.applyTransformation("{\"fo\":\"Foo\", \"bat\":42, \"bau\":123456789}", + ObjectToDeserialize.class)); } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java index 0e1fd0f5c..1d09fbeda 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,16 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; public class ObjectToDeserialize { - public ObjectToDeserialize() { - } - private String foo; private int bar; private long baz; + public ObjectToDeserialize() { + } public String getFoo() { return foo; 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 index 6b6548071..39e69f9e0 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,8 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; -import java.util.Base64; +package software.amazon.lambda.powertools.parameters.transform; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -25,6 +20,11 @@ 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; @@ -90,7 +90,9 @@ public void performComplexTransformation_noTransformer_shouldThrowException() { public void performComplexTransformation_shouldPerformTransformation() { manager.setTransformer(json); - ObjectToDeserialize object = manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); + ObjectToDeserialize object = + manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(object).isNotNull(); } diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index f21ecb412..2a57f21e3 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -82,6 +96,10 @@ <skip>true</skip> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + </plugin> </plugins> </build> diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java index 2b06c9256..ae97232b0 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; public class EventDeserializationException extends RuntimeException { diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java index f1b248fae..22712e8ce 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -30,17 +35,12 @@ import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.util.List; import java.util.Map; import java.util.stream.Collectors; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Class that can be used to extract the meaningful part of an event and deserialize it into a Java object.<br/> @@ -136,12 +136,14 @@ public static EventPart extractDataFrom(Object object) { .map(r -> decode(r.getData())) .collect(Collectors.toList())); } else if (object instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { - KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) object; + KinesisAnalyticsFirehoseInputPreprocessingEvent event = + (KinesisAnalyticsFirehoseInputPreprocessingEvent) object; return new EventPart(event.getRecords().stream() .map(r -> decode(r.getData())) .collect(Collectors.toList())); } else if (object instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { - KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) object; + KinesisAnalyticsStreamsInputPreprocessingEvent event = + (KinesisAnalyticsStreamsInputPreprocessingEvent) object; return new EventPart(event.getRecords().stream() .map(r -> decode(r.getData())) .collect(Collectors.toList())); @@ -181,8 +183,9 @@ private EventPart(Object content) { /** * Deserialize this part of event from JSON to an object of type T + * * @param clazz the target type for deserialization - * @param <T> type of object to return + * @param <T> type of object to return * @return an Object of type T (deserialized from the content) */ public <T> T as(Class<T> clazz) { @@ -201,7 +204,8 @@ public <T> T as(Class<T> clazz) { return (T) contentObject; } if (contentList != null) { - throw new EventDeserializationException("The content of this event is a list, consider using 'asListOf' instead"); + throw new EventDeserializationException( + "The content of this event is a list, consider using 'asListOf' instead"); } // should not occur, except if the event is malformed (missing fields) throw new IllegalStateException("Event content is null: the event may be malformed (missing fields)"); @@ -212,14 +216,16 @@ public <T> T as(Class<T> clazz) { /** * Deserialize this part of event from JSON to a list of objects of type T + * * @param clazz the target type for deserialization - * @param <T> type of object to return + * @param <T> type of object to return * @return a list of objects of type T (deserialized from the content) */ public <T> List<T> asListOf(Class<T> clazz) { if (contentList == null && content == null) { if (contentMap != null || contentObject != null) { - throw new EventDeserializationException("The content of this event is not a list, consider using 'as' instead"); + throw new EventDeserializationException( + "The content of this event is not a list, consider using 'as' instead"); } // should not occur, except if the event is really malformed throw new IllegalStateException("Event content is null: the event may be malformed (missing fields)"); @@ -229,16 +235,20 @@ public <T> List<T> asListOf(Class<T> clazz) { try { return reader.readValue(content); } catch (JsonProcessingException e) { - throw new EventDeserializationException("Cannot load the event as a list of " + clazz.getSimpleName() + ", consider using 'as' instead", e); + throw new EventDeserializationException( + "Cannot load the event as a list of " + clazz.getSimpleName() + + ", consider using 'as' instead", e); } } else { - return contentList.stream().map(s -> { - try { - return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); - } catch (IOException e) { - throw new EventDeserializationException("Cannot load the event as a list of " + clazz.getSimpleName(), e); - } - }).collect(Collectors.toList()); + return contentList.stream().map(s -> + { + try { + return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); + } catch (IOException e) { + throw new EventDeserializationException( + "Cannot load the event as a list of " + clazz.getSimpleName(), e); + } + }).collect(Collectors.toList()); } } } 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 549418263..f5a6d8c11 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 @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; import com.fasterxml.jackson.databind.JsonNode; @@ -25,19 +26,7 @@ import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; public class JsonConfig { - private JsonConfig() { - } - - private static class ConfigHolder { - private final static JsonConfig instance = new JsonConfig(); - } - - public static JsonConfig get() { - return ConfigHolder.instance; - } - private static final ThreadLocal<ObjectMapper> om = ThreadLocal.withInitial(ObjectMapper::new); - private final FunctionRegistry defaultFunctions = FunctionRegistry.defaultRegistry(); private final FunctionRegistry customFunctions = defaultFunctions.extend( new Base64Function(), @@ -49,6 +38,12 @@ public static JsonConfig get() { .withFunctionRegistry(customFunctions) .build(); private JmesPath<JsonNode> jmesPath = new JacksonRuntime(configuration, getObjectMapper()); + private JsonConfig() { + } + + public static JsonConfig get() { + return ConfigHolder.instance; + } /** * Return an Object Mapper. Use this to customize (de)serialization config. @@ -73,7 +68,7 @@ public JmesPath<JsonNode> getJmesPath() { * {@link Base64Function} and {@link Base64GZipFunction} are already built-in. * * @param function the function to add - * @param <T> Must extends {@link BaseFunction} + * @param <T> Must extends {@link BaseFunction} */ public <T extends BaseFunction> void addFunction(T function) { FunctionRegistry functionRegistryWithExtendedFunctions = configuration.functionRegistry().extend(function); @@ -84,4 +79,8 @@ public <T extends BaseFunction> void addFunction(T function) { jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); } + + private static class ConfigHolder { + private final static JsonConfig instance = new JsonConfig(); + } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java index 737d96835..26b655fbd 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static java.nio.charset.StandardCharsets.UTF_8; + import io.burt.jmespath.Adapter; import io.burt.jmespath.JmesPathType; import io.burt.jmespath.function.ArgumentConstraints; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; - import java.nio.ByteBuffer; import java.util.Base64; import java.util.List; -import static java.nio.charset.StandardCharsets.UTF_8; - /** * Function used by JMESPath to decode a Base64 encoded String into a decoded String */ @@ -34,16 +34,6 @@ public Base64Function() { super("powertools_base64", ArgumentConstraints.typeOf(JmesPathType.STRING)); } - @Override - protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> arguments) { - T value = arguments.get(0).value(); - String encodedString = runtime.toString(value); - - String decodedString = decode(encodedString); - - return runtime.createString(decodedString); - } - public static String decode(String encodedString) { return new String(decode(encodedString.getBytes(UTF_8)), UTF_8); } @@ -55,4 +45,14 @@ public static String decode(ByteBuffer byteBuffer) { public static byte[] decode(byte[] encoded) { return Base64.getDecoder().decode(encoded); } + + @Override + protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> arguments) { + T value = arguments.get(0).value(); + String encodedString = runtime.toString(value); + + String decodedString = decode(encodedString); + + return runtime.createString(decodedString); + } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java index 8628fd1d2..f5d5beeb1 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,25 +11,24 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; + import io.burt.jmespath.Adapter; import io.burt.jmespath.JmesPathType; import io.burt.jmespath.function.ArgumentConstraints; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; - import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Arrays; import java.util.List; import java.util.zip.GZIPInputStream; -import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; - /** * Function used by JMESPath to decode a Base64 encoded GZipped String into a decoded String */ @@ -39,20 +38,6 @@ public Base64GZipFunction() { super("powertools_base64_gzip", ArgumentConstraints.typeOf(JmesPathType.STRING)); } - @Override - protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> arguments) { - T value = arguments.get(0).value(); - String encodedString = runtime.toString(value); - - String decompressString = decompress(decode(encodedString.getBytes(UTF_8))); - - if (decompressString == null) { - return runtime.createNull(); - } - - return runtime.createString(decompressString); - } - public static String decompress(byte[] compressed) { if (compressed == null || compressed.length == 0) { return null; @@ -77,6 +62,21 @@ public static String decompress(byte[] compressed) { } public static boolean isCompressed(final byte[] compressed) { - return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)); + return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && + (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)); + } + + @Override + protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> arguments) { + T value = arguments.get(0).value(); + String encodedString = runtime.toString(value); + + String decompressString = decompress(decode(encodedString.getBytes(UTF_8))); + + if (decompressString == null) { + return runtime.createNull(); + } + + return runtime.createString(decompressString); } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java index 584b544bf..b7661b5af 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; import io.burt.jmespath.Adapter; @@ -18,7 +19,6 @@ import io.burt.jmespath.function.ArgumentConstraints; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; - import java.util.List; public class JsonFunction extends BaseFunction { 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 5055d7086..fcfdb47e3 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 @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -29,20 +34,15 @@ 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 software.amazon.lambda.powertools.utilities.model.Order; import software.amazon.lambda.powertools.utilities.model.Product; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; - public class EventDeserializerTest { @Test @@ -61,7 +61,8 @@ public void testDeserializeStringAsObject_shouldReturnObject() { @Test public void testDeserializeStringArrayAsList_shouldReturnList() { - String productStr = "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; + 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)); @@ -254,7 +255,8 @@ public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEv @ParameterizedTest @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) - public void testDeserializeKasipEventMessageAsListShouldReturnList(KinesisAnalyticsStreamsInputPreprocessingEvent event) { + public void testDeserializeKasipEventMessageAsListShouldReturnList( + KinesisAnalyticsStreamsInputPreprocessingEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -262,7 +264,8 @@ public void testDeserializeKasipEventMessageAsListShouldReturnList(KinesisAnalyt @ParameterizedTest @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) - public void testDeserializeKafipEventMessageAsListShouldReturnList(KinesisAnalyticsFirehoseInputPreprocessingEvent event) { + public void testDeserializeKafipEventMessageAsListShouldReturnList( + KinesisAnalyticsFirehoseInputPreprocessingEvent event) { List<Product> products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); 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 5f243537c..d86af6671 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,24 +11,26 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static org.assertj.core.api.Assertions.assertThat; + 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; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public 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)"); + 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 8e574eba6..eeb605076 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,26 +11,27 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static org.assertj.core.api.Assertions.assertThat; + 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; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class Base64GZipFunctionTest { @Test public void testConstructor() { Base64GZipFunction base64GZipFunction = new Base64GZipFunction(); assertThat(base64GZipFunction.name()).isEqualTo("powertools_base64_gzip"); - assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo(JmesPathType.STRING.name().toLowerCase()); + assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo( + JmesPathType.STRING.name().toLowerCase()); assertThat(base64GZipFunction.argumentConstraints().minArity()).isEqualTo(1); assertThat(base64GZipFunction.argumentConstraints().maxArity()).isEqualTo(1); @@ -38,8 +39,10 @@ public void testConstructor() { @Test public 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)"); + 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)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{ \"id\": 43242, \"name\": \"FooBar XY\", \"price\": 258}"); @@ -47,7 +50,8 @@ public void testPowertoolsGzip() throws IOException { @Test public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression<JsonNode> expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip('')"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.NULL); @@ -55,7 +59,8 @@ public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { @Test public void testPowertoolsGzipWrongArgumentType() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + 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)"); JsonNode result = expression.search(event); @@ -70,8 +75,10 @@ public void testBase64GzipDecompressNull() { @Test public 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)"); + 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)"); 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 4ea4eed35..0bfb635fa 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 @@ -1,20 +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.utilities.jmespath; +import static org.assertj.core.api.Assertions.assertThat; + 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; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class JsonFunctionTest { @Test public void testJsonFunction() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression<JsonNode> expression = JsonConfig.get().getJmesPath().compile("powertools_json(body)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.OBJECT); @@ -25,7 +39,8 @@ public void testJsonFunction() throws IOException { @Test public void testJsonFunctionChild() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); + 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"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java index 228089c52..4bf427a21 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.model; import java.util.ArrayList; @@ -21,19 +22,19 @@ public class Basket { private List<Product> products = new ArrayList<>(); - public List<Product> getProducts() { - return products; + public Basket() { } - public void setProducts(List<Product> products) { - this.products = products; + public Basket(Product... p) { + products.addAll(Arrays.asList(p)); } - public Basket() { + public List<Product> getProducts() { + return products; } - public Basket( Product ...p){ - products.addAll(Arrays.asList(p)); + public void setProducts(List<Product> products) { + this.products = products; } public void add(Product product) { @@ -42,8 +43,12 @@ public void add(Product product) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Basket basket = (Basket) o; return products.equals(basket.products); } diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java index eca36c222..6b48ccd1d 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.model; import java.util.HashMap; diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java index f03f6d426..c90a4632e 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.model; import java.util.Objects; @@ -57,8 +58,12 @@ public void setPrice(double price) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + 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); } diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 618aa948c..d8afac783 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -117,4 +131,13 @@ </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 index 85231a003..7adc2afe5 100644 --- 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 @@ -1,15 +1,27 @@ -package software.amazon.lambda.powertools.sqs; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import com.amazonaws.services.lambda.runtime.events.SQSEvent; +package software.amazon.lambda.powertools.sqs; import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.*; +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)} @@ -47,6 +59,7 @@ public <T> SQSBatchProcessingException(final List<Exception> exceptions, /** * 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() { @@ -55,6 +68,7 @@ public List<Exception> getExceptions() { /** * List of returns from {@link SqsMessageHandler#process(SQSMessage)} that were successfully processed. + * * @return List of returns from successfully processed messages */ public List<Object> successMessageReturnValues() { @@ -63,6 +77,7 @@ public List<Object> successMessageReturnValues() { /** * Details of {@link SQSMessage} that failed in {@link SqsMessageHandler#process(SQSMessage)} + * * @return List of failed messages */ public List<SQSMessage> getFailures() { 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 index cd529ff22..d0ffe6a73 100644 --- 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 @@ -1,14 +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.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; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.*; - /** * {@link SqsBatch} is used to process batch messages in {@link SQSEvent} * @@ -22,7 +35,7 @@ * 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> @@ -35,14 +48,14 @@ * <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 @@ -69,6 +82,7 @@ * * ... * </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) 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 index d96245006..847dd456c 100644 --- 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 @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.sqs; import java.lang.annotation.ElementType; @@ -58,7 +72,6 @@ * * <p>To disable deletion of payloads setting the following annotation parameter * {@code @SqsLargeMessage(deletePayloads=false)}</p> - * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) 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 index 17e37797c..0c8f03ee9 100644 --- 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 @@ -1,9 +1,23 @@ -package software.amazon.lambda.powertools.sqs; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import com.amazonaws.services.lambda.runtime.events.SQSEvent; +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 @@ -20,6 +34,7 @@ * <li>{@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)}</li> * </ul> * </p> + * * @param <R> Return value type from {@link SqsMessageHandler#process(SQSMessage)} */ @FunctionalInterface 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 index 9fff4dc6f..8c06a6291 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,30 +11,28 @@ * 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.LinkedList; import java.util.List; -import java.util.Queue; import java.util.function.Function; import java.util.stream.Collectors; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.lambda.powertools.sqs.exception.SkippedMessageDueToFailedBatchException; import software.amazon.lambda.powertools.sqs.internal.BatchContext; -import software.amazon.payloadoffloading.PayloadS3Pointer; import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.processMessages; +import software.amazon.payloadoffloading.PayloadS3Pointer; /** * A class of helper functions to add additional functionality to {@link SQSEvent} processing. @@ -43,11 +41,10 @@ public final class SqsUtils { private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); private static final ObjectMapper objectMapper = new ObjectMapper(); - private static SqsClient client; - private static S3Client s3Client; - // The attribute on an SQS-FIFO message used to record the message group ID private static final String MESSAGE_GROUP_ID = "MessageGroupId"; + private static SqsClient client; + private static S3Client s3Client; private SqsUtils() { } @@ -176,23 +173,24 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * 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> - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - * @param event {@link SQSEvent} received by lambda function. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. + * + * @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 public static <R> List<R> batchProcessor(final SQSEvent event, @@ -264,26 +262,26 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * 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> - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> * - * @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 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 public static <R> List<R> batchProcessor(final SQSEvent event, @@ -325,28 +323,29 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * 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> - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - * @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 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. + * @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 public static <R> List<R> batchProcessor(final SQSEvent event, @@ -356,7 +355,8 @@ public static <R> List<R> batchProcessor(final SQSEvent event, final Class<? extends Exception>... nonRetryableExceptions) { SqsMessageHandler<R> handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance, deleteNonRetryableMessageFromQueue, nonRetryableExceptions); + return batchProcessor(event, suppressException, handlerInstance, deleteNonRetryableMessageFromQueue, + nonRetryableExceptions); } /** @@ -423,23 +423,24 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * 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> - * @see <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html">Amazon SQS dead-letter queues</a> - * @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 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 public static <R> List<R> batchProcessor(final SQSEvent event, @@ -491,7 +492,7 @@ public static <R> List<R> batchProcessor(final SQSEvent event, final Class<? extends Exception>... nonRetryableExceptions) { final List<R> handlerReturn = new ArrayList<>(); - if(client == null) { + if (client == null) { client = SqsClient.create(); } @@ -521,7 +522,8 @@ public static <R> List<R> batchProcessor(final SQSEvent event, 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" + 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; } @@ -534,14 +536,16 @@ public static <R> List<R> batchProcessor(final SQSEvent event, 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()); - }); + .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); + batchContext.processSuccessAndHandleFailed(handlerReturn, suppressException, deleteNonRetryableMessageFromQueue, + nonRetryableExceptions); return handlerReturn; } @@ -552,7 +556,8 @@ private static <R> SqsMessageHandler<R> instantiatedHandler(final Class<? extend return handler.getDeclaredConstructor().newInstance(); } - final Constructor<? extends SqsMessageHandler<R>> constructor = handler.getDeclaredConstructor(handler.getDeclaringClass()); + final Constructor<? extends SqsMessageHandler<R>> constructor = + handler.getDeclaredConstructor(handler.getDeclaringClass()); constructor.setAccessible(true); return constructor.newInstance(handler.getDeclaringClass().getDeclaredConstructor().newInstance()); } catch (Exception e) { @@ -576,7 +581,7 @@ public static ObjectMapper objectMapper() { } public static S3Client s3Client() { - if(null == s3Client) { + if (null == s3Client) { SqsUtils.s3Client = S3Client.create(); } diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java index 9dbb66509..fbb4289d8 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.sqs.exception; /** 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 index 1e4eff3bf..57ddeb22f 100644 --- 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 @@ -1,5 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; @@ -9,8 +30,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.SdkBytes; @@ -28,11 +47,6 @@ import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; import software.amazon.lambda.powertools.sqs.SqsUtils; -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; - 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<>(); @@ -69,16 +83,17 @@ public final <T> void processSuccessAndHandleFailed(final List<T> successReturns 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); - } - }); + 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); @@ -126,7 +141,8 @@ private boolean isNonRetryableException(Exception exception, Class<? extends Exc .anyMatch(aClass -> aClass.isInstance(exception)); } - private boolean moveNonRetryableMessagesToDlqIfConfigured(Map<SQSMessage, Exception> nonRetryableMessageToException) { + private boolean moveNonRetryableMessagesToDlqIfConfigured( + Map<SQSMessage, Exception> nonRetryableMessageToException) { Optional<String> dlqUrl = fetchDlqUrl(nonRetryableMessageToException); if (!dlqUrl.isPresent()) { @@ -134,76 +150,88 @@ private boolean moveNonRetryableMessagesToDlqIfConfigured(Map<SQSMessage, Except } List<SendMessageBatchRequestEntry> dlqMessages = nonRetryableMessageToException.keySet().stream() - .map(sqsMessage -> { - Map<String, MessageAttributeValue> messageAttributesMap = new HashMap<>(); + .map(sqsMessage -> + { + Map<String, MessageAttributeValue> messageAttributesMap = new HashMap<>(); - sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> { - MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); + sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> + { + MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); - builder - .dataType(messageAttribute.getDataType()) - .stringValue(messageAttribute.getStringValue()); + builder + .dataType(messageAttribute.getDataType()) + .stringValue(messageAttribute.getStringValue()); - if (null != messageAttribute.getBinaryValue()) { - builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); - } + if (null != messageAttribute.getBinaryValue()) { + builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); + } - messageAttributesMap.put(s, builder.build()); - }); + messageAttributesMap.put(s, builder.build()); + }); - return SendMessageBatchRequestEntry.builder() - .messageBody(sqsMessage.getBody()) - .id(sqsMessage.getMessageId()) - .messageAttributes(messageAttributesMap) - .build(); - }) + return SendMessageBatchRequestEntry.builder() + .messageBody(sqsMessage.getBody()) + .id(sqsMessage.getMessageId()) + .messageAttributes(messageAttributesMap) + .build(); + }) .collect(toList()); - List<SendMessageBatchResponse> sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> { + List<SendMessageBatchResponse> sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> + { - SendMessageBatchResponse sendMessageBatchResponse = client.sendMessageBatch(SendMessageBatchRequest.builder() - .entries(entriesToSend) - .queueUrl(dlqUrl.get()) - .build()); + SendMessageBatchResponse sendMessageBatchResponse = + client.sendMessageBatch(SendMessageBatchRequest.builder() + .entries(entriesToSend) + .queueUrl(dlqUrl.get()) + .build()); - LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); + LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); - return 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; + .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); - })); + .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() { @@ -213,23 +241,25 @@ private boolean hasFailures() { 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(); + 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); + DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); - LOG.debug("Response from delete request {}", deleteMessageBatchResponse); + LOG.debug("Response from delete request {}", deleteMessageBatchResponse); - return deleteMessageBatchResponse; - }); + return deleteMessageBatchResponse; + }); } } 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 index 588d434d7..7022e399a 100644 --- 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 @@ -1,13 +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.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 com.amazonaws.services.lambda.runtime.Context; -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; @@ -24,63 +42,32 @@ import software.amazon.lambda.powertools.sqs.SqsLargeMessage; import software.amazon.payloadoffloading.PayloadS3Pointer; -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; - @Aspect public class SqsLargeMessageAspect { private static final Logger LOG = LoggerFactory.getLogger(SqsLargeMessageAspect.class); - @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 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; - }); + .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); @@ -100,31 +87,38 @@ private static String readStringFromS3Object(ResponseInputStream<GetObjectRespon 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); + 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; - }); + 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) { + 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); + 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); + throw new FailedProcessingLargePayloadException( + format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", + pointer.getS3BucketName(), pointer.getS3Key()), e); } } @@ -134,6 +128,36 @@ public static boolean placedOnSqsEventRequestHandler(ProceedingJoinPoint pjp) { && 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); 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 index 73e91c3a7..ff0b5b014 100644 --- 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 @@ -1,5 +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.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; @@ -7,10 +25,6 @@ import org.aspectj.lang.annotation.Pointcut; import software.amazon.lambda.powertools.sqs.SqsBatch; -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; - @Aspect public class SqsMessageBatchProcessorAspect { diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java index d48cded5f..557aa214d 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.sqs; import com.amazonaws.services.lambda.runtime.events.SQSEvent; 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 index 43a089d2c..42e4b9d8f 100644 --- 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 @@ -1,14 +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.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.Collection; import java.util.HashMap; import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -20,20 +43,6 @@ 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.SendMessageBatchRequestEntry; - -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; class SqsUtilsBatchProcessorTest { @@ -51,10 +60,11 @@ void setUp() throws IOException { @Test void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { - List<String> returnValues = batchProcessor(event, false, (message) -> { - interactionClient.listQueues(); - return "Success"; - }); + List<String> returnValues = batchProcessor(event, false, (message) -> + { + interactionClient.listQueues(); + return "Success"; + }); assertThat(returnValues) .hasSize(2) @@ -66,7 +76,8 @@ void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { @ParameterizedTest @ValueSource(classes = {SampleInnerSqsHandler.class, SampleSqsHandler.class}) - void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess(Class<? extends SqsMessageHandler<String>> handler) { + void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess( + Class<? extends SqsMessageHandler<String>> handler) { List<String> returnValues = batchProcessor(event, handler); assertThat(returnValues) @@ -80,33 +91,35 @@ void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess(Class<? extend void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - SqsMessageHandler<String> failedHandler = (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } + SqsMessageHandler<String> failedHandler = (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new RuntimeException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }; + interactionClient.listQueues(); + return "Success"; + }; assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains(failedId); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains(failedId); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); verify(interactionClient).listQueues(); @@ -119,29 +132,31 @@ void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { @Test void shouldBatchProcessAndFullFailuresInBatch() { - SqsMessageHandler<String> failedHandler = (message) -> { - throw new RuntimeException(message.getMessageId()); - }; + SqsMessageHandler<String> failedHandler = (message) -> + { + throw new RuntimeException(message.getMessageId()); + }; assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .isEmpty(); + assertThat(e.successMessageReturnValues()) + .isEmpty(); - assertThat(e.getFailures()) - .hasSize(2) - .extracting("messageId") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); + 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"); - }); + assertThat(e.getExceptions()) + .hasSize(2) + .extracting("detailMessage") + .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", + "2e1424d4-f796-459a-8184-9c92662be6da"); + }); verifyNoInteractions(sqsClient); } @@ -150,22 +165,23 @@ void shouldBatchProcessAndFullFailuresInBatch() { void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); } @@ -175,14 +191,15 @@ void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { void shouldBatchProcessAndSuppressExceptions() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - SqsMessageHandler<String> failedHandler = (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } + SqsMessageHandler<String> failedHandler = (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new RuntimeException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }; + interactionClient.listQueues(); + return "Success"; + }; List<String> returnValues = batchProcessor(event, true, failedHandler); @@ -206,16 +223,6 @@ void shouldBatchProcessViaClassAndSuppressExceptions() { verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); } - public class SampleInnerSqsHandler implements SqsMessageHandler<String> { - private int counter; - - @Override - public String process(SQSMessage message) { - interactionClient.listQueues(); - return String.valueOf(counter++); - } - } - @Test void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; @@ -226,18 +233,20 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() .attributes(attributes) - .build()); + .build()); - List<String> batchProcessor = batchProcessor(event, (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } + List<String> batchProcessor = batchProcessor(event, (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new IllegalStateException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }, IllegalStateException.class, IllegalArgumentException.class); + interactionClient.listQueues(); + return "Success"; + }, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -255,18 +264,20 @@ void shouldBatchProcessAndDeleteNonRetryableException() { " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + 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"); - } + 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); + interactionClient.listQueues(); + return "Success"; + }, true, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -277,26 +288,28 @@ void shouldBatchProcessAndDeleteNonRetryableException() { @Test void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); + SQSEvent batch25Message = + MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(batch25Message, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .hasSize(24) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(24) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); ArgumentCaptor<DeleteMessageBatchRequest> captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); @@ -310,7 +323,8 @@ void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { @Test void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); + SQSEvent batch25Message = + MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); HashMap<QueueAttributeName, String> attributes = new HashMap<>(); @@ -319,18 +333,20 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() t " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + 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"; - } + 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); + throw new IllegalStateException("Failed processing"); + }, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -346,6 +362,16 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() t .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) { 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 index cfa79dc36..53beeefcb 100644 --- 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 @@ -1,30 +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.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 com.fasterxml.jackson.databind.ObjectMapper; +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.*; +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; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - public class SqsUtilsFifoBatchProcessorTest { private static SQSEvent sqsBatchEvent; @@ -61,10 +75,11 @@ public void tearDown() { public void processWholeBatch() { // Act AtomicInteger processedCount = new AtomicInteger(); - List<Object> results = batchProcessor(sqsBatchEvent, false, (message) -> { - processedCount.getAndIncrement(); - return true; - }); + List<Object> results = batchProcessor(sqsBatchEvent, false, (message) -> + { + processedCount.getAndIncrement(); + return true; + }); // Assert assertThat(processedCount.get()).isEqualTo(3); @@ -80,31 +95,34 @@ public void processWholeBatch() { @Test public void singleFailureInMiddleOfBatch() { // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())).thenReturn(DeleteMessageBatchResponse - .builder().build()); + 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 + .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()); - }); + .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() @@ -119,20 +137,22 @@ public void singleFailureInMiddleOfBatch() { public void singleFailureAtEndOfBatch() { // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())).thenReturn(DeleteMessageBatchResponse - .builder().build()); + 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; - })); + .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> + { + int value = processedCount.getAndIncrement(); + if (value == 2) { + throw new RuntimeException("Whoops"); + } + return true; + })); // Assert DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); @@ -150,17 +170,19 @@ 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(); - }); + .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 index 48de3e6a9..d3b675371 100644 --- 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 @@ -1,13 +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.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 com.amazonaws.services.lambda.runtime.events.SQSEvent; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,23 +52,21 @@ import software.amazon.awssdk.utils.StringInputStream; import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; -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; - class SqsUtilsLargeMessageTest { - @Mock - private S3Client s3Client; 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() { @@ -54,16 +76,21 @@ void setUp() { @Test public void testLargeMessage() { - ResponseInputStream<GetObjectResponse> s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); + 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 + "\"}]"); + 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; - }); + 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) @@ -74,29 +101,35 @@ public void testLargeMessage() { verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); + .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + 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()))); + 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 + "\"}]"); + 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; - }); + 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) @@ -107,13 +140,14 @@ public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { 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); - }); + .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); + + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } else { verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); } @@ -121,17 +155,20 @@ public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { @Test public void shouldNotProcessSmallMessageBody() { - ResponseInputStream<GetObjectResponse> s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); + 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; - }); + 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"); @@ -144,7 +181,9 @@ public void shouldNotProcessSmallMessageBody() { 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 + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) @@ -156,16 +195,20 @@ public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exce @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"); - } - })); + 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 + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) @@ -175,15 +218,6 @@ public void close() throws IOException { verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); } - private static Stream<Arguments> exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); - } - private SQSEvent messageWithBody(String messageBody) { SQSMessage sqsMessage = new SQSMessage(); sqsMessage.setBody(messageBody); diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java index b0d8177ac..3bad9644f 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java @@ -1,11 +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.sqs.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.SqsLargeMessage; import software.amazon.lambda.powertools.sqs.SampleSqsHandler; import software.amazon.lambda.powertools.sqs.SqsBatch; +import software.amazon.lambda.powertools.sqs.SqsLargeMessage; public class LambdaHandlerApiGateway implements RequestHandler<APIGatewayProxyRequestEvent, String> { 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 index 63f1573bf..172179057 100644 --- 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 @@ -1,14 +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.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; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class PartialBatchFailureSuppressedHandler implements RequestHandler<SQSEvent, String> { @Override @SqsBatch(value = InnerMessageHandler.class, suppressException = true) 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 index 653459d82..6e3971269 100644 --- 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 @@ -1,14 +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.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; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class PartialBatchPartialFailureHandler implements RequestHandler<SQSEvent, String> { @Override @SqsBatch(InnerMessageHandler.class) 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 index 926cdb4f5..acfcd7109 100644 --- 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 @@ -1,14 +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.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; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class PartialBatchSuccessHandler implements RequestHandler<SQSEvent, String> { @Override @SqsBatch(InnerMessageHandler.class) diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java index ee8c100e6..de096679f 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; import com.amazonaws.services.lambda.runtime.Context; 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 index 6eec87301..74ff02e2c 100644 --- 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 @@ -1,18 +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.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; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class SqsMessageHandlerWithNonRetryableHandler implements RequestHandler<SQSEvent, String> { @Override - @SqsBatch(value = InnerMessageHandler.class, nonRetryableExceptions = {IllegalStateException.class, IllegalArgumentException.class}) + @SqsBatch(value = InnerMessageHandler.class, nonRetryableExceptions = {IllegalStateException.class, + IllegalArgumentException.class}) public String handleRequest(final SQSEvent sqsEvent, final Context context) { return "Success"; @@ -22,11 +37,11 @@ private class InnerMessageHandler implements SqsMessageHandler<Object> { @Override public String process(SQSMessage message) { - if(message.getMessageId().isEmpty()) { + if (message.getMessageId().isEmpty()) { throw new IllegalArgumentException("Invalid message and was moved to DLQ"); } - if("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { + if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { throw new RuntimeException("Invalid message and should be reprocessed"); } 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 index 789a7b86d..5b341880e 100644 --- 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 @@ -1,14 +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.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; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class SqsMessageHandlerWithNonRetryableHandlerWithDelete implements RequestHandler<SQSEvent, String> { @Override @@ -24,11 +38,11 @@ private class InnerMessageHandler implements SqsMessageHandler<Object> { @Override public String process(SQSMessage message) { - if(message.getMessageId().isEmpty()) { + if (message.getMessageId().isEmpty()) { throw new IllegalArgumentException("Invalid message and was moved to DLQ"); } - if("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { + if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { throw new RuntimeException("Invalid message and should be reprocessed"); } diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java index 337592004..e96dc5581 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; import com.amazonaws.services.lambda.runtime.Context; 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 index 22844ab4c..ff04aba25 100644 --- 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 @@ -1,14 +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.sqs.internal; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.function.Consumer; -import java.util.stream.Stream; +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; @@ -31,30 +56,24 @@ import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandler; import software.amazon.lambda.powertools.sqs.handlers.SqsNoDeleteMessageHandler; -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; - 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 final String BUCKET_NAME = "bucketname"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; + 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() { @@ -67,7 +86,9 @@ void setUp() { @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 + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); String response = requestHandler.handleRequest(sqsEvent, context); @@ -79,13 +100,14 @@ public void testLargeMessage() { 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); - }); + .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); + + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } @Test @@ -107,7 +129,9 @@ public void shouldNotProcessSmallMessageBody() { 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 + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(FailedProcessingLargePayloadException.class) @@ -122,7 +146,9 @@ 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 + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); String response = requestHandler.handleRequest(sqsEvent, context); @@ -131,19 +157,22 @@ public void testLargeMessageWithDeletionOff() { 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"); - } - })); + 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 + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(FailedProcessingLargePayloadException.class) @@ -157,7 +186,9 @@ public void close() throws IOException { public void shouldNotDoAnyProcessingWhenNotSqsEvent() { LambdaHandlerApiGateway handler = new LambdaHandlerApiGateway(); - String messageBody = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody(messageBody); @@ -170,16 +201,8 @@ public void shouldNotDoAnyProcessingWhenNotSqsEvent() { } private ResponseInputStream<GetObjectResponse> s3ObjectWithLargeMessage() { - return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - } - - private static Stream<Arguments> exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); + return new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); } private SQSEvent messageWithBody(String messageBody) { 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 index a65aa486b..b257c1962 100644 --- 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 @@ -1,12 +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.sqs.internal; -import java.io.IOException; -import java.util.HashMap; +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; @@ -27,28 +53,14 @@ import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandler; import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandlerWithDelete; -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; - 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; - private final Context context = mock(Context.class); - @BeforeEach void setUp() throws IOException { overrideSqsClient(sqsClient); @@ -73,21 +85,22 @@ void shouldBatchProcessMessageWithSuccessDeletedOnFailureInBatchFromSQS() { 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"); - }); + .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)); @@ -124,9 +137,10 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); requestHandler.handleRequest(event, context); @@ -140,13 +154,14 @@ void shouldBatchProcessAndThrowExceptionForNonRetryableExceptionWhenMoveToDlqRet requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); event.getRecords().get(0).setMessageId(""); - when(sqsClient.sendMessageBatch(any(SendMessageBatchRequest.class))).thenReturn(SendMessageBatchResponse.builder() + when(sqsClient.sendMessageBatch(any(SendMessageBatchRequest.class))).thenReturn( + SendMessageBatchResponse.builder() .failed(BatchResultErrorEntry.builder() .message("Permission Error") .code("KMS.AccessDeniedException") .senderFault(true) .build()) - .build()); + .build()); HashMap<QueueAttributeName, String> attributes = new HashMap<>(); @@ -155,9 +170,10 @@ void shouldBatchProcessAndThrowExceptionForNonRetryableExceptionWhenMoveToDlqRet " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); Assertions.assertThatExceptionOfType(SQSBatchProcessingException.class). isThrownBy(() -> requestHandler.handleRequest(event, context)); @@ -192,28 +208,31 @@ void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); event.getRecords().get(0).setMessageId(""); - event.getRecords().forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp")); + event.getRecords() + .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp")); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .build()); + 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"); - }); + .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)); @@ -224,33 +243,36 @@ void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionWhenFailedParsingPolicy() { requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); event.getRecords().get(0).setMessageId(""); - event.getRecords().forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp-queue")); + 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()); + 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"); - }); + .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)); @@ -271,27 +293,29 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqAndThrowException() thro " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + 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"); - }); + .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)); diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 5737296da..4a79d2987 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -1,4 +1,18 @@ <?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"> 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 index 7c3e79112..55349b267 100644 --- 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 @@ -1,6 +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.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; @@ -12,13 +41,6 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Map; - -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 org.apache.logging.log4j.Level; import org.apache.logging.log4j.ThreadContext; import org.junit.jupiter.api.AfterEach; @@ -36,15 +58,6 @@ import software.amazon.lambda.powertools.testsuite.handler.LoggingOrderMessageHandler; import software.amazon.lambda.powertools.testsuite.handler.TracingLoggingStreamMessageHandler; -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; - public class LoggingOrderTest { private static final String BUCKET_NAME = "ms-extended-sqs-client"; @@ -79,25 +92,30 @@ void tearDown() { * 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()))); + 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 + "\"}]"); + 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)); + .satisfies(line -> + { + Map<String, Object> actual = parseToMap(line.get(0)); - String message = actual.get("message").toString(); + String message = actual.get("message").toString(); - assertThat(message) - .contains("A big message"); - }); + assertThat(message) + .contains("A big message"); + }); } @Test @@ -107,7 +125,8 @@ public void testLoggingAnnotationActsAfterTracingForStreamingHandler() throws IO S3EventNotification s3EventNotification = s3EventNotification(); TracingLoggingStreamMessageHandler handler = new TracingLoggingStreamMessageHandler(); - handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); + handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), + output, context); assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isNotEmpty(); @@ -121,7 +140,8 @@ private void setupContext() { when(context.getAwsRequestId()).thenReturn("RequestId"); } - private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + private void resetLogLevel(Level level) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); resetLogLevels.setAccessible(true); resetLogLevels.invoke(null, level); @@ -138,25 +158,27 @@ private Map<String, Object> parseToMap(String stringAsJson) { } 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") - ); + 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)); } diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java index a85c81b1d..5592b1fd3 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.testsuite.handler; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java index d0f2b3ac5..4a60d0949 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java @@ -1,13 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.handler; +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.util.Map; - -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.tracing.Tracing; diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 42a229f42..b5de90f7b 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -113,4 +127,13 @@ </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-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java index 9fd09e8ee..29d10d188 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.tracing; public enum CaptureMode { 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 cb90f3315..6f17a2e33 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing; import java.lang.annotation.ElementType; @@ -39,7 +40,7 @@ * to a sub segment named after the method.</p> * * <p>To disable this functionality you can specify {@code @Tracing( captureError = false)}</p> - *e + * e * <p>All traces have a namespace set. If {@code @Tracing( namespace = "ExampleService")} is set * this takes precedent over any value set in the environment variable {@code POWER_TOOLS_SERVICE_NAME}. * If both are undefined then the value will default to {@code service_undefined}</p> @@ -48,6 +49,7 @@ @Target(ElementType.METHOD) 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 @@ -55,6 +57,7 @@ */ @Deprecated boolean captureResponse() default true; + /** * @deprecated As of release 1.2.0, replaced by captureMode() * in order to support different modes and support via @@ -62,6 +65,8 @@ */ @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 0e956e539..9fb021548 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing; -import java.util.function.Consumer; +import static software.amazon.lambda.powertools.core.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 static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import java.util.function.Consumer; /** * A class of helper functions to add additional functionality and ease * of use. - * */ public final class TracingUtils { private static ObjectMapper objectMapper; @@ -32,7 +32,7 @@ public final class TracingUtils { /** * Put an annotation to the current subsegment with a String value. * - * @param key the key of the annotation + * @param key the key of the annotation * @param value the value of the annotation */ public static void putAnnotation(String key, String value) { @@ -43,33 +43,33 @@ public static void putAnnotation(String key, String value) { /** * Put an annotation to the current subsegment with a Number value. * - * @param key the key of the annotation + * @param key the key of the annotation * @param value the value of the annotation */ public static void putAnnotation(String key, Number value) { AWSXRay.getCurrentSubsegmentOptional() - .ifPresent(segment -> segment.putAnnotation(key, value)); + .ifPresent(segment -> segment.putAnnotation(key, value)); } /** * Put an annotation to the current subsegment with a Boolean value. * - * @param key the key of the annotation + * @param key the key of the annotation * @param value the value of the annotation */ public static void putAnnotation(String key, Boolean value) { AWSXRay.getCurrentSubsegmentOptional() - .ifPresent(segment -> segment.putAnnotation(key, value)); + .ifPresent(segment -> segment.putAnnotation(key, value)); } /** * Put additional metadata for the current subsegment. - * + * <p> * The namespace used will be the namespace of the current subsegment if it * is set else it will follow the namespace process as described in * {@link Tracing} * - * @param key the key of the metadata + * @param key the key of the metadata * @param value the value of the metadata */ public static void putMetadata(String key, Object value) { @@ -83,8 +83,8 @@ public static void putMetadata(String key, Object value) { * Put additional metadata for the current subsegment. * * @param namespace the namespace of the metadata - * @param key the key of the metadata - * @param value the value of the metadata + * @param key the key of the metadata + * @param value the value of the metadata */ public static void putMetadata(String namespace, String key, Object value) { AWSXRay.getCurrentSubsegmentOptional() @@ -94,14 +94,14 @@ public static void putMetadata(String namespace, String key, Object value) { /** * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. - * + * <p> * The namespace used follows the flow as described in {@link Tracing} - * + * <p> * This method is intended for use with multi-threaded programming where the * context is lost between threads. * - * @param name the name of the subsegment - * @param entity the current x-ray context + * @param name the name of the subsegment + * @param entity the current x-ray context * @param subsegment the x-ray subsegment for the wrapped consumer */ public static void withEntitySubsegment(String name, Entity entity, Consumer<Subsegment> subsegment) { @@ -112,16 +112,17 @@ public static void withEntitySubsegment(String name, Entity entity, Consumer<Sub /** * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. - * + * <p> * This method is intended for use with multi-threaded programming where the * context is lost between threads. * - * @param namespace the namespace of the subsegment - * @param name the name of the subsegment - * @param entity the current x-ray context + * @param namespace the namespace of the subsegment + * @param name the name of the subsegment + * @param entity the current x-ray context * @param subsegment the x-ray subsegment for the wrapped consumer */ - public static void withEntitySubsegment(String namespace, String name, Entity entity, Consumer<Subsegment> subsegment) { + public static void withEntitySubsegment(String namespace, String name, Entity entity, + Consumer<Subsegment> subsegment) { AWSXRay.setTraceEntity(entity); withSubsegment(namespace, name, subsegment); } @@ -129,10 +130,10 @@ public static void withEntitySubsegment(String namespace, String name, Entity en /** * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. - * + * <p> * The namespace used follows the flow as described in {@link Tracing} * - * @param name the name of the subsegment + * @param name the name of the subsegment * @param subsegment the x-ray subsegment for the wrapped consumer */ public static void withSubsegment(String name, Consumer<Subsegment> subsegment) { @@ -143,8 +144,8 @@ public static void withSubsegment(String name, Consumer<Subsegment> subsegment) * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. * - * @param namespace the namespace for the subsegment - * @param name the name of the subsegment + * @param namespace the namespace for the subsegment + * @param name the name of the subsegment * @param subsegment the x-ray subsegment for the wrapped consumer */ public static void withSubsegment(String namespace, String name, Consumer<Subsegment> subsegment) { 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 26feec66b..62416fce6 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,24 +11,25 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.internal; -import java.util.function.Supplier; +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.tracing.TracingUtils.objectMapper; + import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Subsegment; +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 software.amazon.lambda.powertools.tracing.Tracing; -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.tracing.TracingUtils.objectMapper; - @Aspect public final class LambdaTracingAspect { @SuppressWarnings({"EmptyMethod"}) @@ -42,8 +43,8 @@ public Object around(ProceedingJoinPoint pjp, Object[] proceedArgs = pjp.getArgs(); Subsegment segment = AWSXRay.beginSubsegment( - customSegmentNameOrDefault(tracing, - () -> "## " + pjp.getSignature().getName())); + customSegmentNameOrDefault(tracing, + () -> "## " + pjp.getSignature().getName())); segment.setNamespace(namespace(tracing)); if (isHandlerMethod(pjp)) { @@ -57,7 +58,8 @@ public Object around(ProceedingJoinPoint pjp, try { Object methodReturn = pjp.proceed(proceedArgs); if (captureResponse) { - segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", null != objectMapper() ? objectMapper().writeValueAsString(methodReturn): methodReturn); + segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", + null != objectMapper() ? objectMapper().writeValueAsString(methodReturn) : methodReturn); } if (isHandlerMethod(pjp)) { @@ -67,7 +69,8 @@ public Object around(ProceedingJoinPoint pjp, return methodReturn; } catch (Exception e) { if (captureError) { - segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " error", null != objectMapper() ? objectMapper().writeValueAsString(e) : e); + segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " error", + null != objectMapper() ? objectMapper().writeValueAsString(e) : e); } throw e; } finally { @@ -81,7 +84,8 @@ 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") ? captureResponse : + powerToolsTracing.captureResponse(); case RESPONSE: case RESPONSE_AND_ERROR: return true; @@ -95,7 +99,8 @@ 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") ? captureError : + powerToolsTracing.captureError(); case ERROR: case RESPONSE_AND_ERROR: return true; diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java index da1a92ced..c66b8b7ee 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.tracing.internal; public class SystemWrapper { 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 d2a96ec65..69054d0c6 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing; +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; @@ -20,12 +27,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -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; - class TracingUtilsTest { @BeforeEach @@ -51,12 +52,12 @@ void shouldSetAnnotationOnCurrentSubSegment() { TracingUtils.putAnnotation("booleanKey", false); assertThat(AWSXRay.getTraceEntity().getAnnotations()) - .hasSize(3) - .contains( - entry("stringKey", "val"), - entry("numberKey", 10), - entry("booleanKey", false) - ); + .hasSize(3) + .contains( + entry("stringKey", "val"), + entry("numberKey", 10), + entry("booleanKey", false) + ); } @Test @@ -94,60 +95,64 @@ void shouldNotSetMetaDataIfNoCurrentSubSegment() { void shouldInvokeCodeBlockWrappedWithinSubsegment() { Context test = mock(Context.class); - TracingUtils.withSubsegment("testSubSegment", subsegment -> { - subsegment.putAnnotation("key", "val"); - subsegment.putMetadata("key", "val"); - test.getFunctionName(); - }); + 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 -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("service_undefined"); + assertThat(subsegment.getNamespace()) + .isEqualTo("service_undefined"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); - assertThat(subsegment.getMetadata()) - .hasSize(1); - }); + assertThat(subsegment.getMetadata()) + .hasSize(1); + }); } @Test void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { Context test = mock(Context.class); - TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> { - subsegment.putAnnotation("key", "val"); - subsegment.putMetadata("key", "val"); - test.getFunctionName(); - }); + 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 -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("testNamespace"); + assertThat(subsegment.getNamespace()) + .isEqualTo("testNamespace"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); - assertThat(subsegment.getMetadata()) - .hasSize(1); - }); + assertThat(subsegment.getMetadata()) + .hasSize(1); + }); } @Test @@ -156,10 +161,11 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce Entity traceEntity = AWSXRay.getTraceEntity(); - Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> { - subsegment.putAnnotation("key", "val"); - test.getFunctionName(); - })); + Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> + { + subsegment.putAnnotation("key", "val"); + test.getFunctionName(); + })); thread.start(); thread.join(); @@ -168,17 +174,18 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); - - assertThat(subsegment.getNamespace()) - .isEqualTo("service_undefined"); - - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); + + assertThat(subsegment.getNamespace()) + .isEqualTo("service_undefined"); + + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); + }); } @Test @@ -187,10 +194,12 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter Entity traceEntity = AWSXRay.getTraceEntity(); - Thread thread = new Thread(() -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> { - subsegment.putAnnotation("key", "val"); - test.getFunctionName(); - })); + Thread thread = + new Thread(() -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> + { + subsegment.putAnnotation("key", "val"); + test.getFunctionName(); + })); thread.start(); thread.join(); @@ -199,16 +208,17 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); - - assertThat(subsegment.getNamespace()) - .isEqualTo("testNamespace"); - - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); + + assertThat(subsegment.getNamespace()) + .isEqualTo("testNamespace"); + + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); + }); } } \ No newline at end of file diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java index 78878ffe5..1a9dc851a 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java index 80f37b8b6..49bc4e095 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.tracing.handlers; -import java.io.InputStream; -import java.io.OutputStream; +package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.InputStream; +import java.io.OutputStream; public class PowerToolDisabledForStream implements RequestStreamHandler { diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java index 3be79fb76..5af3c65af 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java index cd026f427..88b42c690 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE_AND_ERROR; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE_AND_ERROR; - public class PowerTracerToolEnabledExplicitlyForResponseAndError implements RequestHandler<Object, Object> { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java index c84d25763..47e23ed18 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.ERROR; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.ERROR; - public class PowerTracerToolEnabledForError implements RequestHandler<Object, Object> { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java index 1e82f2148..be0fe9b24 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; - public class PowerTracerToolEnabledForResponse implements RequestHandler<Object, Object> { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java index b7c908473..a18b1580d 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,9 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; -import java.io.IOException; +import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.fasterxml.jackson.core.JsonGenerator; @@ -21,11 +23,10 @@ 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.tracing.Tracing; import software.amazon.lambda.powertools.tracing.TracingUtils; -import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; - public class PowerTracerToolEnabledForResponseWithCustomMapper implements RequestHandler<Object, Object> { static { ObjectMapper objectMapper = new ObjectMapper(); @@ -35,6 +36,7 @@ public class PowerTracerToolEnabledForResponseWithCustomMapper implements Reques TracingUtils.defaultObjectMapper(objectMapper); } + @Override @Tracing(namespace = "lambdaHandler", captureMode = RESPONSE) public Object handleRequest(Object input, Context context) { @@ -44,6 +46,25 @@ public Object handleRequest(Object input, Context context) { return parentClass; } + public static class ChildSerializer extends StdSerializer<ChildClass> { + + public ChildSerializer() { + this(null); + } + + public ChildSerializer(Class<ChildClass> t) { + super(t); + } + + @Override + public void serialize(ChildClass value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeStartObject(); + jgen.writeStringField("name", value.name); + jgen.writeStringField("p", value.p.name); + jgen.writeEndObject(); + } + } + public class ParentClass { public String name; public ChildClass c; @@ -66,23 +87,4 @@ public ChildClass(String name, ParentClass p) { this.p = p; } } - - public static class ChildSerializer extends StdSerializer<ChildClass> { - - public ChildSerializer() { - this(null); - } - - public ChildSerializer(Class<ChildClass> t) { - super(t); - } - - @Override - public void serialize(ChildClass value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStartObject(); - jgen.writeStringField("name", value.name); - jgen.writeStringField("p", value.p.name); - jgen.writeEndObject(); - } - } } diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java index e316ffe7d..5c693445a 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import software.amazon.lambda.powertools.tracing.Tracing; - import java.io.InputStream; import java.io.OutputStream; +import software.amazon.lambda.powertools.tracing.Tracing; public class PowerTracerToolEnabledForStream implements RequestStreamHandler { diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java index 4cd381807..4cd2bb0b7 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,17 +11,17 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; -import java.io.InputStream; -import java.io.OutputStream; +import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; 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.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; - public class PowerTracerToolEnabledForStreamWithNoMetaData implements RequestStreamHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java index cc184d020..88c506502 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java index 2109d6647..b4d77cde3 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; - public class PowerTracerToolEnabledWithNoMetaData implements RequestHandler<Object, Object> { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java index 2ded5e69f..c13f28df0 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; - public class PowerTracerToolEnabledWithNoMetaDataDeprecated implements RequestHandler<Object, Object> { @Override 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 8cd9b2f71..5e3ec6545 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,24 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.internal; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; +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.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; @@ -41,14 +50,6 @@ import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaDataDeprecated; import software.amazon.lambda.powertools.tracing.nonhandler.PowerToolNonHandler; -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.catchThrowable; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - class LambdaTracingAspectTest { private RequestHandler<Object, Object> requestHandler; private RequestStreamHandler streamHandler; @@ -118,16 +119,17 @@ void shouldCaptureTraces() { 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"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } @Test @@ -141,20 +143,21 @@ void shouldCaptureTracesWithExceptionMetaData() { 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)); - }); + .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)); + }); } @Test @@ -166,16 +169,17 @@ void shouldCaptureTracesForStream() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "streamHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("streamHandler"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "streamHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("streamHandler"); + }); } @Test @@ -207,15 +211,16 @@ void shouldCaptureTracesWithNoMetadata() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -229,15 +234,16 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -251,15 +257,16 @@ void shouldCaptureTracesWithNoMetadataDeprecated() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -275,15 +282,16 @@ void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } } @@ -300,16 +308,17 @@ void shouldCaptureTracesIfExplicitlyEnabledAndEnvironmentVariableIsDisabled() { 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"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } } @@ -324,14 +333,16 @@ void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); - assertThat(subsegment.getMetadata().get("lambdaHandler")) - .hasFieldOrPropertyWithValue("handleRequest response", "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}"); - }); + assertThat(subsegment.getMetadata().get("lambdaHandler")) + .hasFieldOrPropertyWithValue("handleRequest response", + "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}"); + }); assertThatNoException().isThrownBy(AWSXRay::endSegment); @@ -354,16 +365,17 @@ void shouldCaptureTracesIfExplicitlyEnabledBothAndEnvironmentVariableIsDisabled( 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"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } } @@ -384,15 +396,16 @@ void shouldNotCaptureTracesWithExceptionMetaDataIfDisabledViaEnvironmentVariable assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } } @@ -410,20 +423,21 @@ void shouldCaptureTracesWithExceptionMetaDataEnabledExplicitlyAndEnvironmentVari 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)); - }); + .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/nonhandler/PowerToolNonHandler.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java index 309eb5598..48ffc93ae 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java @@ -1,3 +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. + * + */ + package software.amazon.lambda.powertools.tracing.nonhandler; import software.amazon.lambda.powertools.tracing.Tracing; diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 1469183ef..daec3aa99 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -1,4 +1,18 @@ <?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"> @@ -119,4 +133,13 @@ </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-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java index 68260cb47..f41364c1a 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; +import static com.networknt.schema.SpecVersion.VersionFlag.V7; + import com.amazonaws.services.lambda.runtime.Context; import com.networknt.schema.SpecVersion.VersionFlag; - import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.ElementType; @@ -23,8 +25,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static com.networknt.schema.SpecVersion.VersionFlag.V7; - /** * {@link Validation} is used to specify that the annotated method input and/or output needs to be valid.<br> * 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 b29adf8d7..baf5e2465 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; import com.fasterxml.jackson.databind.JsonNode; @@ -26,7 +27,7 @@ /** * Use this if you need to customize some part of the JSON Schema validation * (eg. specification version, Jackson ObjectMapper, or adding functions to JMESPath). - * + * <p> * For everything but the validation features (factory, schemaVersion), {@link ValidationConfig} * is just a wrapper of {@link JsonConfig}. */ @@ -62,7 +63,7 @@ public void setSchemaVersion(SpecVersion.VersionFlag version) { * {@link Base64Function} and {@link Base64GZipFunction} are already built-in. * * @param function the function to add - * @param <T> Must extend {@link BaseFunction} + * @param <T> Must extend {@link BaseFunction} */ public <T extends BaseFunction> void addFunction(T function) { JsonConfig.get().addFunction(function); diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java index 2d3e1b350..fd4cb66a6 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; public class ValidationException extends RuntimeException { 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 3c2322edc..4eecb3ab5 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; @@ -22,8 +23,6 @@ import com.networknt.schema.JsonSchema; import com.networknt.schema.ValidationMessage; import io.burt.jmespath.Expression; -import software.amazon.lambda.powertools.validation.internal.ValidationAspect; - import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.Collections; @@ -31,6 +30,7 @@ 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 @@ -68,7 +68,8 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope) } JsonNode subNode; try { - PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(obj.getClass(), ClassLoader.getSystemClassLoader()); + PojoSerializer pojoSerializer = + LambdaEventSerializers.serializerFor(obj.getClass(), ClassLoader.getSystemClassLoader()); ByteArrayOutputStream out = new ByteArrayOutputStream(); pojoSerializer.toJson(obj, out); JsonNode jsonNode = ValidationConfig.get().getObjectMapper().readTree(out.toString("UTF-8")); @@ -89,7 +90,8 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope) try { validate(subNode.asText(), jsonSchema); } catch (ValidationException e) { - throw new ValidationException("Invalid format for '" + envelope + "': 'STRING' and no JSON found in it."); + throw new ValidationException( + "Invalid format for '" + envelope + "': 'STRING' and no JSON found in it."); } } else { throw new ValidationException("Invalid format for '" + envelope + "': '" + subNode.getNodeType() + "'"); @@ -208,9 +210,11 @@ public static void validate(JsonNode jsonNode, JsonSchema jsonSchema) throws Val if (!validationMessages.isEmpty()) { String message; try { - message = ValidationConfig.get().getObjectMapper().writeValueAsString(new ValidationErrors(validationMessages)); + message = ValidationConfig.get().getObjectMapper() + .writeValueAsString(new ValidationErrors(validationMessages)); } catch (JsonProcessingException e) { - message = validationMessages.stream().map(ValidationMessage::getMessage).collect(Collectors.joining(", ")); + message = validationMessages.stream().map(ValidationMessage::getMessage) + .collect(Collectors.joining(", ")); } throw new ValidationException(message); } @@ -255,7 +259,8 @@ private static JsonSchema createJsonSchema(String schema) { jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream); } catch (Exception e) { - throw new IllegalArgumentException("'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); + throw new IllegalArgumentException( + "'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); } } else { jsonSchema = ValidationConfig.get().getFactory().getSchema(schema); @@ -270,7 +275,8 @@ private static void validateSchema(String schema, JsonSchema jsonSchema) { validate(jsonSchema.getSchemaNode(), getJsonSchema("classpath:/schemas/meta_schema_" + version)); } catch (ValidationException ve) { - throw new IllegalArgumentException("The schema " + schema + " is not valid, it does not respect the specification " + version, ve); + throw new IllegalArgumentException( + "The schema " + schema + " is not valid, it does not respect the specification " + version, 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 e659abbeb..6055f8d58 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,17 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.internal; +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.utilities.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; +import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; +import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; @@ -43,14 +52,6 @@ import software.amazon.lambda.powertools.validation.Validation; import software.amazon.lambda.powertools.validation.ValidationConfig; -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.utilities.jmespath.Base64Function.decode; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; -import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; -import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; - /** * Aspect for {@link Validation} annotation */ @@ -108,27 +109,33 @@ 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)); + event.getRecords() + .forEach(record -> validate(decode(record.getKinesis().getData()), inboundJsonSchema)); } else if (obj instanceof KinesisFirehoseEvent) { KinesisFirehoseEvent event = (KinesisFirehoseEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof KafkaEvent) { KafkaEvent event = (KafkaEvent) obj; - event.getRecords().forEach((s, records) -> records.forEach(record -> validate(decode(record.getValue()), inboundJsonSchema))); + event.getRecords().forEach((s, records) -> records.forEach( + record -> validate(decode(record.getValue()), inboundJsonSchema))); } else if (obj instanceof ActiveMQEvent) { ActiveMQEvent event = (ActiveMQEvent) obj; event.getMessages().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof RabbitMQEvent) { RabbitMQEvent event = (RabbitMQEvent) obj; - event.getRmqMessagesByQueue().forEach((s, records) -> records.forEach(record -> validate(decode(record.getData()), inboundJsonSchema))); + event.getRmqMessagesByQueue().forEach((s, records) -> records.forEach( + record -> validate(decode(record.getData()), inboundJsonSchema))); } else if (obj instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { - KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj; + KinesisAnalyticsFirehoseInputPreprocessingEvent event = + (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { - KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) obj; + KinesisAnalyticsStreamsInputPreprocessingEvent event = + (KinesisAnalyticsStreamsInputPreprocessingEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else { - LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", obj.getClass().getName()); + LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", + obj.getClass().getName()); } } } @@ -151,10 +158,12 @@ public Object around(ProceedingJoinPoint pjp, ApplicationLoadBalancerResponseEvent response = (ApplicationLoadBalancerResponseEvent) result; validate(response.getBody(), outboundJsonSchema); } else if (result instanceof KinesisAnalyticsInputPreprocessingResponse) { - KinesisAnalyticsInputPreprocessingResponse response = (KinesisAnalyticsInputPreprocessingResponse) result; + 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()); + LOG.warn("Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", + result.getClass().getName()); } } 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 6669e46b1..86dddd3e2 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 @@ -1,25 +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.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; +import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; + import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.SpecVersion; +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.validation.model.Basket; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; import software.amazon.lambda.powertools.validation.model.Product; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -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.assertj.core.api.Assertions.assertThatThrownBy; -import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; -import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; - public class ValidationUtilsTest { private String schemaString = "classpath:/schema_v7.json"; @@ -43,16 +56,18 @@ public void testLoadSchemaV7KO() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); 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"); + .hasMessage( + "The schema classpath:/schema_v7_ko.json is not valid, it does not respect the specification V7"); } @Test public void testLoadMetaSchema_NoValidation() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); - assertThatNoException().isThrownBy(() -> { - getJsonSchema("classpath:/schema_v7_ko.json", false); - }); + assertThatNoException().isThrownBy(() -> + { + getJsonSchema("classpath:/schema_v7_ko.json", false); + }); } @Test @@ -99,16 +114,19 @@ public void testLoadSchemaNotFound() { @Test public void testValidateJsonNodeOK() throws IOException { - JsonNode node = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ok.json")); + JsonNode node = + ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ok.json")); - assertThatNoException().isThrownBy(() -> { - validate(node, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(node, schemaString); + }); } @Test public void testValidateJsonNodeKO() throws IOException { - JsonNode node = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ko.json")); + JsonNode node = + ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ko.json")); assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(node, schema)); } @@ -121,9 +139,10 @@ public void testValidateMapOK() { map.put("name", "FooBar XY"); map.put("price", 258); - assertThatNoException().isThrownBy(() -> { - validate(map, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(map, schemaString); + }); } @Test @@ -147,9 +166,10 @@ public void testValidateMapNotValidJsonObject() { public void testValidateStringOK() { String json = "{\n \"id\": 43242,\n \"name\": \"FooBar XY\",\n \"price\": 258\n}"; - assertThatNoException().isThrownBy(() -> { - validate(json, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(json, schemaString); + }); } @Test @@ -163,9 +183,10 @@ public void testValidateStringKO() { public void testValidateObjectOK() { Product product = new Product(42, "FooBar", 42); - assertThatNoException().isThrownBy(() -> { - validate(product, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(product, schemaString); + }); } @Test @@ -189,9 +210,10 @@ public void testValidateSubObjectOK() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - assertThatNoException().isThrownBy(() -> { - validate(event, schemaString, "basket.products[0]"); - }); + assertThatNoException().isThrownBy(() -> + { + validate(event, schemaString, "basket.products[0]"); + }); } @Test @@ -203,7 +225,8 @@ public void testValidateSubObjectKO() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(event, schema, "basket.products[0]")); + assertThatExceptionOfType(ValidationException.class).isThrownBy( + () -> validate(event, schema, "basket.products[0]")); } @Test @@ -227,7 +250,8 @@ public void testValidateSubObjectListKO() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(event, schema, "basket.products[*]")); + assertThatExceptionOfType(ValidationException.class).isThrownBy( + () -> validate(event, schema, "basket.products[*]")); } @Test 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/GenericSchemaV7Handler.java index 1c64e7da1..5b8343d1b 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/GenericSchemaV7Handler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java index 07954ddff..6989cdbb6 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java index 9eb96c0e8..c49ebff69 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java index c1859f29a..d3f46d4ad 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; 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/ValidationInboundStringHandler.java index 2e62ba88d..fd5692884 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/ValidationInboundStringHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; 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 9df0ff508..b634d6f8c 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 @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.internal; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; @@ -18,15 +19,14 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayV2WebSocketResponse; import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent; import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; - import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; 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; public class ResponseEventsArgumentsProvider implements ArgumentsProvider { @@ -49,9 +49,11 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) { KinesisAnalyticsInputPreprocessingResponse kaipResponse = new KinesisAnalyticsInputPreprocessingResponse(); List records = new ArrayList<KinesisAnalyticsInputPreprocessingResponse.Record>(); ByteBuffer buffer = ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8)); - records.add(new KinesisAnalyticsInputPreprocessingResponse.Record("1", KinesisAnalyticsInputPreprocessingResponse.Result.Ok, buffer)); + records.add(new KinesisAnalyticsInputPreprocessingResponse.Record("1", + KinesisAnalyticsInputPreprocessingResponse.Result.Ok, buffer)); kaipResponse.setRecords(records); - return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse, apiGWV2WebSocketResponse, albResponseEvent, kaipResponse).map(Arguments::of); + return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse, 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 63e93c3ac..9ea596ff3 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.internal; +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.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; @@ -34,6 +40,8 @@ import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; import com.networknt.schema.SpecVersion; +import java.io.IOException; +import java.util.stream.Stream; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.junit.jupiter.api.BeforeEach; @@ -53,14 +61,6 @@ import software.amazon.lambda.powertools.validation.handlers.ValidationInboundStringHandler; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; -import java.io.IOException; -import java.util.stream.Stream; - -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.mockito.Mockito.when; - public class ValidationAspectTest { @@ -109,9 +109,10 @@ public void testValidateOutboundJsonSchema(Object object) throws Throwable { when(validation.inboundSchema()).thenReturn(""); when(validation.outboundSchema()).thenReturn("classpath:/schema_v7.json"); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> { - validationAspect.around(pjp, validation); - }); + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> + { + validationAspect.around(pjp, validation); + }); } @Test @@ -184,7 +185,8 @@ public void validate_inputKO_schemaInString_shouldThrowValidationException() { @Test public void validate_SQS() { - PojoSerializer<SQSEvent> pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer<SQSEvent> pojoSerializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); @@ -193,7 +195,8 @@ public void validate_SQS() { @Test public void validate_SQS_CustomEnvelopeTakePrecedence() { - PojoSerializer<SQSEvent> pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer<SQSEvent> pojoSerializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); SQSWithCustomEnvelopeHandler handler = new SQSWithCustomEnvelopeHandler(); @@ -202,7 +205,8 @@ public void validate_SQS_CustomEnvelopeTakePrecedence() { @Test public void validate_SQS_WrongEnvelope_shouldThrowValidationException() { - PojoSerializer<SQSEvent> pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer<SQSEvent> pojoSerializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); SQSWithWrongEnvelopeHandler handler = new SQSWithWrongEnvelopeHandler(); @@ -211,7 +215,8 @@ public void validate_SQS_WrongEnvelope_shouldThrowValidationException() { @Test public void validate_Kinesis() { - PojoSerializer<KinesisEvent> pojoSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer<KinesisEvent> pojoSerializer = + LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); KinesisEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/kinesis.json")); GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); @@ -221,7 +226,8 @@ public void validate_Kinesis() { @ParameterizedTest @MethodSource("provideEventAndEventType") public void validateEEvent(String jsonResource, Class eventClass) throws IOException { - Object event = ValidationConfig.get().getObjectMapper().readValue(this.getClass().getResourceAsStream(jsonResource), eventClass); + Object event = ValidationConfig.get().getObjectMapper() + .readValue(this.getClass().getResourceAsStream(jsonResource), eventClass); GenericSchemaV7Handler<Object> handler = new GenericSchemaV7Handler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java index 398f67265..881090bdc 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.model; import java.util.ArrayList; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java index 12f3f99ca..04c7c3a4a 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.model; public class MyCustomEvent { diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java index fde888b76..93f5ab39f 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.model; public class Product { From 8548397747be3dfc70eea10b7a3084f6ce15c637 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden <jeromevdl@gmail.com> Date: Thu, 27 Jul 2023 11:39:50 +0200 Subject: [PATCH 0395/1008] few formatting things --- .../test/java/software/amazon/lambda/powertools/TracingE2ET.java | 1 - .../powertools/logging/internal/AbstractJacksonLayoutCopy.java | 1 + .../lambda/powertools/parameters/internal/AnotherObject.java | 1 + .../powertools/parameters/transform/ObjectToDeserialize.java | 1 + .../software/amazon/lambda/powertools/utilities/JsonConfig.java | 1 + 5 files changed, 4 insertions(+), 1 deletion(-) 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 cd4a21df4..043de5494 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 @@ -33,7 +33,6 @@ public class TracingE2ET { private static final String service = "TracingE2EService_" + UUID.randomUUID(); - // "TracingE2EService_e479fb27-422b-4107-9f8c-086c62e1cd12"; private static Infrastructure infrastructure; private static String functionName; 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 index f98e2ee46..17d09729f 100644 --- 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 @@ -56,6 +56,7 @@ abstract class AbstractJacksonLayoutCopy extends AbstractStringLayout { protected final boolean complete; protected final boolean includeNullDelimiter; protected final ResolvableKeyValuePair[] additionalFields; + @Deprecated protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, final Charset charset, diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java index 074a08844..5ce2b355f 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java @@ -18,6 +18,7 @@ public class AnotherObject { private String another; private int object; + public AnotherObject() { } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java index 1d09fbeda..8b30858fd 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java @@ -19,6 +19,7 @@ public class ObjectToDeserialize { private String foo; private int bar; private long baz; + public ObjectToDeserialize() { } 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 f5a6d8c11..baa6a0367 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 @@ -38,6 +38,7 @@ public class JsonConfig { .withFunctionRegistry(customFunctions) .build(); private JmesPath<JsonNode> jmesPath = new JacksonRuntime(configuration, getObjectMapper()); + private JsonConfig() { } From 0671bbeaf2366502fb603ae6c79dbb1865bb2067 Mon Sep 17 00:00:00 2001 From: Alexey Soshin <alexey.soshin@gmail.com> Date: Thu, 27 Jul 2023 12:42:02 +0100 Subject: [PATCH 0396/1008] Adding external examples from https://github.com/aws/aws-sam-cli-app-templates (#1318) --- examples/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 1869b4e8f..b44ff2433 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ 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. -## The Examples +## Examples * [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API @@ -51,6 +51,17 @@ 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. +### 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) + + ### 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. From 661100c14b712b940d8af85f7623525012ae9466 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:07:41 +0200 Subject: [PATCH 0397/1008] build(deps): bump com.puppycrawl.tools:checkstyle from 10.9.1 to 10.12.1 (#1320) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.9.1 to 10.12.1. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.9.1...checkstyle-10.12.1) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7744913fd..e8f85e3ff 100644 --- a/pom.xml +++ b/pom.xml @@ -564,7 +564,7 @@ <dependency> <groupId>com.puppycrawl.tools</groupId> <artifactId>checkstyle</artifactId> - <version>10.9.1</version> + <version>10.12.1</version> </dependency> </dependencies> <executions> From 3dc84051a1a32cf90b8e3d716fe608e707ee4c16 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub <kozubg@gmail.com> Date: Fri, 28 Jul 2023 08:56:43 +0200 Subject: [PATCH 0398/1008] docs: add support for docs versioning (#1239) (#1293) * docs: add support for docs versioning (#1239) * docs: change configuration to release docs only during the release or by hand. Rollback config for building doc for PR. (#1239) * docs: Uncomment logic responsible for pushing changes to S3.(#1239) --------- Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/docs.yml | 41 ------- .github/workflows/rebuild-latest-docs.yml | 39 ++++++ .github/workflows/release-doc.yml | 19 +++ .github/workflows/reusable-publish-docs.yml | 124 ++++++++++++++++++++ mkdocs.yml | 3 + 6 files changed, 186 insertions(+), 42 deletions(-) delete mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/rebuild-latest-docs.yml create mode 100644 .github/workflows/release-doc.yml create mode 100644 .github/workflows/reusable-publish-docs.yml diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index cbcdcdd9e..15d08587d 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -34,4 +34,4 @@ jobs: - name: Build docs website run: | echo "GIT_PYTHON_REFRESH=quiet" - make build-docs-website \ No newline at end of file + make build-docs-website 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/rebuild-latest-docs.yml b/.github/workflows/rebuild-latest-docs.yml new file mode 100644 index 000000000..853aaa1d3 --- /dev/null +++ b/.github/workflows/rebuild-latest-docs.yml @@ -0,0 +1,39 @@ +name: Rebuild latest docs + +# PROCESS +# +# 1. Build User Guide and API docs +# 2. Publish to GitHub Pages +# 3. Publish to S3 (new home) + +# USAGE +# +# Only used for deploying a documentation hotfix to /latest and its associated version w/o a full release. +# +# Steps: +# +# 1. Trigger "Rebuild latest docs" workflow manually: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow +# 2. Use the latest version released under Releases e.g. 2.0.0 + +on: + workflow_dispatch: + inputs: + latest_published_version: + description: "Latest published version to rebuild latest docs for, e.g. 1.4.2" + default: "1.4.2" + required: true + +permissions: + contents: read + +jobs: + release-docs: + permissions: + contents: write # push to gh-pages + pages: write # deploy gh-pages website + id-token: write # trade JWT token for AWS credentials in AWS Docs account + secrets: inherit + uses: ./.github/workflows/reusable_publish_docs.yml + with: + version: ${{ inputs.latest_published_version }} + alias: latest diff --git a/.github/workflows/release-doc.yml b/.github/workflows/release-doc.yml new file mode 100644 index 000000000..5f87f970a --- /dev/null +++ b/.github/workflows/release-doc.yml @@ -0,0 +1,19 @@ +name: Docs + +on: + release: + types: + - published + workflow_dispatch: {} + +jobs: + release-docs: + permissions: + contents: write + pages: write + id-token: write + secrets: inherit + uses: ./.github/workflows/reusable-publish-docs.yml + with: + version: main + alias: stage \ No newline at end of file diff --git a/.github/workflows/reusable-publish-docs.yml b/.github/workflows/reusable-publish-docs.yml new file mode 100644 index 000000000..9ab53dac2 --- /dev/null +++ b/.github/workflows/reusable-publish-docs.yml @@ -0,0 +1,124 @@ +name: Reusable Publish docs + +env: + BRANCH: main + ORIGIN: aws-powertools/powertools-lambda-java + VERSION: "" + +on: + workflow_call: + inputs: + version: + description: "Version to build and publish docs (1.28.0, develop)" + required: true + type: string + alias: + description: "Alias to associate version (latest, stage)" + required: true + type: string + detached_mode: + description: "Whether it's running in git detached mode to ensure git is sync'd" + required: false + default: false + type: boolean + +permissions: + contents: write + id-token: write + pages: write + +jobs: + publish-docs: + runs-on: ubuntu-latest + environment: Docs + steps: + - name: Checkout code + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + with: + # While `fetch-depth` is used to allow the workflow to later commit & push the changes. + fetch-depth: 0 + - name: Setup dependencies + uses: ./.github/actions/cached-node-modules + - name: Set up Python + uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b # v4.6.0 + with: + python-version: "3.8" + - name: Install doc generation dependencies + run: | + pip install --upgrade pip + pip install -r docs/requirements.txt + - name: Setup doc deploy + run: | + git config --global user.name Docs deploy + git config --global user.email aws-devax-open-source@amazon.com + - name: Git refresh tip (detached mode) + # Git Detached mode (release notes) doesn't have origin + if: ${{ inputs.detached_mode }} + run: | + git config pull.rebase true + git config remote.origin.url >&- || git remote add origin https://github.com/"$ORIGIN" + git pull origin "$BRANCH" + - name: Normalize Version Number + run: echo "VERSION=$(echo ${{ inputs.version }} | sed 's/v//')" >> $GITHUB_ENV + - name: Build docs website and API reference + env: + ALIAS: ${{ inputs.alias }} + run: | + rm -rf site + mkdocs build + mike deploy --update-aliases --no-redirect ${{ env.VERSION }} ${{ env.ALIAS }} --branch backup-gh-pages + # Set latest version as a default + mike set-default latest --branch backup-gh-pages + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef # v2.0.0 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Copy API Docs + run: | + cp -r api site/ + + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.VERSION }}/ + - name: Deploy Docs (Alias) + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.ALIAS }}/ + - name: Deploy Docs (Version JSON) + env: + VERSION: ${{ inputs.version }} + ALIAS: ${{ inputs.alias }} + + + # We originally used "mike" from PyPi to manage versions for us, but since we moved to S3, we can't use it to manage versions any more. + # Instead, we're using some shell script that manages the versions. + # + # Operations: + # 1. Download the versions.json file from S3 + # 2. Find any reference to the alias and delete it from the versions file + # 3. This is voodoo (don't use JQ): + # - we assign the input as $o and the new version/alias as $n, + # - we check if the version number exists in the file already (for republishing docs) + # - if it's an alias (stage/latest/*) or old version, we do nothing and output $o (original input) + # - if it's a new version number, we add it at position 0 in the array. + # 4. Once done, we'll upload it back to S3. + + run: | + aws s3 cp \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-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 == "${{ env.VERSION }}";.) then $o 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/mkdocs.yml b/mkdocs.yml index da00b24d1..05f02c5ca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,6 +82,9 @@ extra_javascript: - javascript/extra.js extra: + version: + provider: mike + default: latest powertools: version: 1.16.1 # to update after each release (we do not want snapshot version here) From a0c7d3860dd86c5f991c21e55177917d7119cd50 Mon Sep 17 00:00:00 2001 From: Grzegorz Kozub <kozubg@gmail.com> Date: Fri, 28 Jul 2023 10:02:09 +0200 Subject: [PATCH 0399/1008] docs: versioning - fix typo (#1322) * docs: add support for docs versioning (#1239) * docs: change configuration to release docs only during the release or by hand. Rollback config for building doc for PR. (#1239) * docs: HotFix - workflow path(#1239) * docs: HotFix - workflow path(#1239) * docs: typo. (#1239) --- .github/workflows/rebuild-latest-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rebuild-latest-docs.yml b/.github/workflows/rebuild-latest-docs.yml index 853aaa1d3..f176ca901 100644 --- a/.github/workflows/rebuild-latest-docs.yml +++ b/.github/workflows/rebuild-latest-docs.yml @@ -33,7 +33,7 @@ jobs: pages: write # deploy gh-pages website id-token: write # trade JWT token for AWS credentials in AWS Docs account secrets: inherit - uses: ./.github/workflows/reusable_publish_docs.yml + uses: ./.github/workflows/reusable-publish-docs.yml with: version: ${{ inputs.latest_published_version }} alias: latest From bddd99a84be8cdacfc18e53c710647434ed058f8 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 28 Jul 2023 09:59:55 +0100 Subject: [PATCH 0400/1008] fix: Rollback doc changes (#1323) * Revert "docs: versioning - fix typo (#1322)" This reverts commit a0c7d3860dd86c5f991c21e55177917d7119cd50. * Revert "docs: add support for docs versioning (#1239) (#1293)" This reverts commit 3dc84051a1a32cf90b8e3d716fe608e707ee4c16. --- .github/workflows/build-docs.yml | 2 +- .github/workflows/docs.yml | 41 +++++++ .github/workflows/rebuild-latest-docs.yml | 39 ------ .github/workflows/release-doc.yml | 19 --- .github/workflows/reusable-publish-docs.yml | 124 -------------------- mkdocs.yml | 3 - 6 files changed, 42 insertions(+), 186 deletions(-) create mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/rebuild-latest-docs.yml delete mode 100644 .github/workflows/release-doc.yml delete mode 100644 .github/workflows/reusable-publish-docs.yml diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 15d08587d..cbcdcdd9e 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -34,4 +34,4 @@ jobs: - name: Build docs website run: | echo "GIT_PYTHON_REFRESH=quiet" - make build-docs-website + make build-docs-website \ No newline at end of file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..9c09e294d --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,41 @@ +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/rebuild-latest-docs.yml b/.github/workflows/rebuild-latest-docs.yml deleted file mode 100644 index f176ca901..000000000 --- a/.github/workflows/rebuild-latest-docs.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Rebuild latest docs - -# PROCESS -# -# 1. Build User Guide and API docs -# 2. Publish to GitHub Pages -# 3. Publish to S3 (new home) - -# USAGE -# -# Only used for deploying a documentation hotfix to /latest and its associated version w/o a full release. -# -# Steps: -# -# 1. Trigger "Rebuild latest docs" workflow manually: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow -# 2. Use the latest version released under Releases e.g. 2.0.0 - -on: - workflow_dispatch: - inputs: - latest_published_version: - description: "Latest published version to rebuild latest docs for, e.g. 1.4.2" - default: "1.4.2" - required: true - -permissions: - contents: read - -jobs: - release-docs: - permissions: - contents: write # push to gh-pages - pages: write # deploy gh-pages website - id-token: write # trade JWT token for AWS credentials in AWS Docs account - secrets: inherit - uses: ./.github/workflows/reusable-publish-docs.yml - with: - version: ${{ inputs.latest_published_version }} - alias: latest diff --git a/.github/workflows/release-doc.yml b/.github/workflows/release-doc.yml deleted file mode 100644 index 5f87f970a..000000000 --- a/.github/workflows/release-doc.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Docs - -on: - release: - types: - - published - workflow_dispatch: {} - -jobs: - release-docs: - permissions: - contents: write - pages: write - id-token: write - secrets: inherit - uses: ./.github/workflows/reusable-publish-docs.yml - with: - version: main - alias: stage \ No newline at end of file diff --git a/.github/workflows/reusable-publish-docs.yml b/.github/workflows/reusable-publish-docs.yml deleted file mode 100644 index 9ab53dac2..000000000 --- a/.github/workflows/reusable-publish-docs.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: Reusable Publish docs - -env: - BRANCH: main - ORIGIN: aws-powertools/powertools-lambda-java - VERSION: "" - -on: - workflow_call: - inputs: - version: - description: "Version to build and publish docs (1.28.0, develop)" - required: true - type: string - alias: - description: "Alias to associate version (latest, stage)" - required: true - type: string - detached_mode: - description: "Whether it's running in git detached mode to ensure git is sync'd" - required: false - default: false - type: boolean - -permissions: - contents: write - id-token: write - pages: write - -jobs: - publish-docs: - runs-on: ubuntu-latest - environment: Docs - steps: - - name: Checkout code - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - with: - # While `fetch-depth` is used to allow the workflow to later commit & push the changes. - fetch-depth: 0 - - name: Setup dependencies - uses: ./.github/actions/cached-node-modules - - name: Set up Python - uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b # v4.6.0 - with: - python-version: "3.8" - - name: Install doc generation dependencies - run: | - pip install --upgrade pip - pip install -r docs/requirements.txt - - name: Setup doc deploy - run: | - git config --global user.name Docs deploy - git config --global user.email aws-devax-open-source@amazon.com - - name: Git refresh tip (detached mode) - # Git Detached mode (release notes) doesn't have origin - if: ${{ inputs.detached_mode }} - run: | - git config pull.rebase true - git config remote.origin.url >&- || git remote add origin https://github.com/"$ORIGIN" - git pull origin "$BRANCH" - - name: Normalize Version Number - run: echo "VERSION=$(echo ${{ inputs.version }} | sed 's/v//')" >> $GITHUB_ENV - - name: Build docs website and API reference - env: - ALIAS: ${{ inputs.alias }} - run: | - rm -rf site - mkdocs build - mike deploy --update-aliases --no-redirect ${{ env.VERSION }} ${{ env.ALIAS }} --branch backup-gh-pages - # Set latest version as a default - mike set-default latest --branch backup-gh-pages - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef # v2.0.0 - with: - aws-region: us-east-1 - role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} - - name: Copy API Docs - run: | - cp -r api site/ - - - name: Deploy Docs (Version) - env: - VERSION: ${{ inputs.version }} - ALIAS: ${{ inputs.alias }} - run: | - aws s3 sync \ - site/ \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.VERSION }}/ - - name: Deploy Docs (Alias) - env: - VERSION: ${{ inputs.version }} - ALIAS: ${{ inputs.alias }} - run: | - aws s3 sync \ - site/ \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.ALIAS }}/ - - name: Deploy Docs (Version JSON) - env: - VERSION: ${{ inputs.version }} - ALIAS: ${{ inputs.alias }} - - - # We originally used "mike" from PyPi to manage versions for us, but since we moved to S3, we can't use it to manage versions any more. - # Instead, we're using some shell script that manages the versions. - # - # Operations: - # 1. Download the versions.json file from S3 - # 2. Find any reference to the alias and delete it from the versions file - # 3. This is voodoo (don't use JQ): - # - we assign the input as $o and the new version/alias as $n, - # - we check if the version number exists in the file already (for republishing docs) - # - if it's an alias (stage/latest/*) or old version, we do nothing and output $o (original input) - # - if it's a new version number, we add it at position 0 in the array. - # 4. Once done, we'll upload it back to S3. - - run: | - aws s3 cp \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-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 == "${{ env.VERSION }}";.) then $o 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/mkdocs.yml b/mkdocs.yml index 05f02c5ca..da00b24d1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,9 +82,6 @@ extra_javascript: - javascript/extra.js extra: - version: - provider: mike - default: latest powertools: version: 1.16.1 # to update after each release (we do not want snapshot version here) From a2ad29251a7ade7433030b3efcb901573c2e5d5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:24:40 +0200 Subject: [PATCH 0401/1008] build(deps): bump aws.sdk.version from 2.20.111 to 2.20.114 (#1324) Bumps `aws.sdk.version` from 2.20.111 to 2.20.114. Updates `software.amazon.awssdk:bom` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:http-client-spi` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:url-connection-client` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:s3` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:lambda` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:cloudwatch` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:xray` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:cloudformation` from 2.20.111 to 2.20.114 Updates `software.amazon.awssdk:sts` from 2.20.111 to 2.20.114 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 843d8b5e8..2de8c75c4 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.111</aws.sdk.version> + <aws.sdk.version>2.20.114</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 78e4b0904..98f7fc9c2 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.111</version> + <version>2.20.114</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index e8f85e3ff..c1ac76f72 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <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.111</aws.sdk.version> + <aws.sdk.version>2.20.114</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 1261e58f837b5f883dcb7214f6a0e0d812f57a40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 11:17:18 +0200 Subject: [PATCH 0402/1008] build(deps): bump org.apache.commons:commons-lang3 from 3.12.0 to 3.13.0 (#1325) Bumps org.apache.commons:commons-lang3 from 3.12.0 to 3.13.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1ac76f72..b0f7030ec 100644 --- a/pom.xml +++ b/pom.xml @@ -263,7 +263,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.12.0</version> + <version>3.13.0</version> <scope>test</scope> </dependency> <dependency> From 70da5f042ca289ee2584e2da89a32026f1a8cfa4 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:23:20 +0100 Subject: [PATCH 0403/1008] docs: Add maintainers guide (#1326) --- docs/processes/maintainers.md | 247 ++++++++++++++++++++++++++++++++++ mkdocs.yml | 2 + 2 files changed, 249 insertions(+) create mode 100644 docs/processes/maintainers.md diff --git a/docs/processes/maintainers.md b/docs/processes/maintainers.md new file mode 100644 index 000000000..6b3d9e126 --- /dev/null +++ b/docs/processes/maintainers.md @@ -0,0 +1,247 @@ +--- +title: Maintainers playbook +description: Process +--- + +<!-- markdownlint-disable MD043 --> + +## Overview + +!!! note "Please treat this content as a living document." + +This is document explains who the maintainers are, their responsibilities, and how they should be doing it. If you're interested in contributing, + see [CONTRIBUTING](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CONTRIBUTING.md){target="_blank"}. + +## 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 | + +## 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 | + +## Labels + +These are the most common labels used by maintainers to triage issues, pull requests (PR), and for project management: + +| Label | Usage | Notes | +|----------------------------------|---------------------------------------------------------------------------------------------------|----------------------------------------------------| +| triage | New issues that require maintainers review | Issue template | +| bug | Unexpected, reproducible and unintended software behavior | PR/Release automation; Doc snippets are excluded; | +| documentation | Documentation improvements | PR/Release automation; Doc additions, fixes, etc.; | +| duplicate | Dupe of another issue | | +| enhancement | New or enhancements to existing features | Issue template | +| RFC | Technical design documents related to a feature request | Issue template | +| help wanted | Tasks you want help from anyone to move forward | Bandwidth, complex topics, etc. | +| feature-parity | Adding features present in other Powertools for Lambda libraries | | +| good first issue | Somewhere for new contributors to start | | +| governance | Issues related to project governance - contributor guides, automation, etc. | | +| question | Issues that are raised to ask questions | | +| maven | Related to the build system | | +| need-more-information | Missing information before making any calls | | +| status/staged-next-release | Changes are merged and will be available once the next release is made. | | +| status/staged-next-major-release | Contains breaking changes - merged changes will be available once the next major release is made. | | +| blocked | Issues or PRs that are blocked for varying reasons | Timeline is uncertain | +| priority:1 | Critical - needs urgent attention | | +| priority:2 | High - core feature, or affects 60%+ of users | | +| priority:3 | Neutral - not a core feature, or affects < 40% of users | | +| priority:4 | Low - nice to have | | +| priority:5 | Low - idea for later | | +| invalid | This doesn't seem right | | +| size/XS | PRs between 0-9 LOC | PR automation | +| size/S | PRs between 10-29 LOC | PR automation | +| size/M | PRs between 30-99 LOC | PR automation | +| size/L | PRs between 100-499 LOC | PR automation | +| size/XL | PRs between 500-999 LOC, often PRs that grown with feedback | PR automation | +| size/XXL | PRs with 1K+ LOC, largely documentation related | PR automation | +| dependencies | Changes that touch dependencies, e.g. Dependabot, etc. | PR/ automation | +| maintenance | Address outstanding tech debt | | + +## Maintainer Responsibilities + +Maintainers are active and visible members of the community, and have +[maintain-level permissions on a repository](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization){target="_blank"}. +Use those privileges to serve the community and evolve code as follows. + +Be aware of recurring ambiguous situations and [document them](#common-scenarios) to help your fellow maintainers. + +### Uphold Code of Conduct + +<!-- markdownlint-disable-next-line MD013 --> +Model the behavior set forward by the +[Code of Conduct](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CODE_OF_CONDUCT.md){target="_blank"} +and raise any violations to other maintainers and admins. There could be unusual circumstances where inappropriate +behavior does not immediately fall within the [Code of Conduct](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CODE_OF_CONDUCT.md){target="_blank"}. + +These might be nuanced and should be handled with extra care - when in doubt, do not engage and reach out to other maintainers +and admins. + +### Prioritize Security + +Security is your number one priority. Maintainer's Github keys must be password protected securely and any reported +security vulnerabilities are addressed before features or bugs. + +Note that this repository is monitored and supported 24/7 by Amazon Security, see +[Security disclosures](https://github.com/aws-powertools/powertools-lambda-java/){target="_blank"} for details. + +### Review Pull Requests + +Review pull requests regularly, comment, suggest, reject, merge and close. Accept only high quality pull-requests. +Provide code reviews and guidance on incoming pull requests. + +PRs are [labeled](#labels) based on file changes and semantic title. Pay attention to whether labels reflect the current +state of the PR and correct accordingly. + +Use and enforce [semantic versioning](https://semver.org/){target="_blank" rel="nofollow"} pull request titles, as these will be used for +[CHANGELOG](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CHANGELOG.md){target="_blank"} +and [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases) - make sure they communicate their +intent at the human level. + +For issues linked to a PR, make sure `status/staged-next-release` label is applied to them when merging. +[Upon release](#releasing-a-new-version), these issues will be notified which release version contains their change. + +See [Common scenarios](#common-scenarios) section for additional guidance. + +### Triage New Issues + +Manage [labels](#labels), review issues regularly, and create new labels as needed by the project. Remove `triage` +label when you're able to confirm the validity of a request, a bug can be reproduced, etc. +Give priority to the original author for implementation, unless it is a sensitive task that is best handled by maintainers. + +Make sure issues are assigned to our [board of activities](https://github.com/orgs/aws-powertools/projects/4). + +Use our [labels](#labels) to signal good first issues to new community members, and to set expectation that this might +need additional feedback from the author, other customers, experienced community members and/or maintainers. + +Be aware of [casual contributors](https://opensource.com/article/17/10/managing-casual-contributors){target="_blank" rel="nofollow"} and recurring contributors. +Provide the experience and attention you wish you had if you were starting in open source. + +See [Common scenarios](#common-scenarios) section for additional guidance. + +### Triage Bug Reports + +Be familiar with [our definition of bug](#is-that-a-bug). If it's not a bug, you can close it or adjust its title and +labels - always communicate the reason accordingly. + +For bugs caused by upstream dependencies, replace `bug` with `bug-upstream` label. Ask the author whether they'd like to +raise the issue upstream or if they prefer us to do so. + +Assess the impact and make the call on whether we need an emergency release. Contact other [maintainers](#current-maintainers) when in doubt. + +See [Common scenarios](#common-scenarios) section for additional guidance. + +### Triage RFCs + +RFC is a collaborative process to help us get to the most optimal solution given the context. Their purpose is to ensure +everyone understands what this context is, their trade-offs, and alternative solutions that were part of the research +before implementation begins. + +Make sure you ask these questions in mind when reviewing: + +- Does it use our [RFC template](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=RFC%2C+triage&projects=&template=rfc.md&title=RFC%3A+)? +- Does it match our [Tenets](https://docs.powertools.aws.dev/lambda/java/latest/#tenets)? +- Does the proposal address the use case? If so, is the recommended usage explicit? +- Does it focus on the mechanics to solve the use case over fine-grained implementation details? +- Can anyone familiar with the code base implement it? +- If approved, are they interested in contributing? Do they need any guidance? +- Does this significantly increase the overall project maintenance? Do we have the skills to maintain it? +- If we can't take this use case, are there alternative projects we could recommend? Or does it call for a new project altogether? + +When necessary, be upfront that the time to review, approve, and implement a RFC can vary - +see [Contribution is stuck](#contribution-is-stuck). Some RFCs may be further updated after implementation, as certain areas become clearer. + +Some examples using our initial and new RFC templates: #92, #94, #95, #991, #1226 + +### Releasing a new version + +!!! note "The release process is currently a long, multi-step process. The team is in the process of automating at it." + +Firstly, make sure the commit history in the `main` branch **(1)** it's up to date, **(2)** commit messages are semantic, +and **(3)** commit messages have their respective area, for example `feat: <change>`, `chore: ...`). + +**Looks good, what's next?** + +Kickoff the `Prepare for maven central release` workflow with the intended rekease version. Once this has completed, it will +draft a Pull Request named something like `chore: Prep release 1.19.0`. the PR will **(1)** roll all of the POM versions +forward to the new release version and **(2)** release notes. + +Once this is done, check out the branch and clean up the release notes. These will be used both in the +[CHANGELOG.md file](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CHANGELOG.md) +file and the [published github release information](https://github.com/aws-powertools/powertools-lambda-java/releases), +and you can use the existing release notes to see how changes are summarized. + +Next, commit and push, wait for the build to complete, and merge to main. Once main has built successfully (i.e. build, tests and end-to-end tests should pass), create a +tagged release from the Github UI, using the same release notes. + +Next, run the `Publish package to the Maven Central Repository` action to release the library. + +Finally, by hand, create a PR rolling all of the POMs onto the next snapshot version (e.g. `1.20.0-SNAPSHOT`). + + +### Add Continuous Integration Checks + +Add integration checks that validate pull requests and pushes to ease the burden on Pull Request reviewers. +Continuously revisit areas of improvement to reduce operational burden in all parties involved. + +### Negative Impact on the Project +<!-- markdownlint-disable-next-line MD013 --> +Actions that negatively impact the project will be handled by the admins, in coordination with other maintainers, +in balance with the urgency of the issue. Examples would be +[Code of Conduct](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CODE_OF_CONDUCT.md){target="_blank"} +violations, deliberate harmful or malicious actions, spam, monopolization, and security risks. + +## Common scenarios + +These are recurring ambiguous situations that new and existing maintainers may encounter. They serve as guidance. +It is up to each maintainer to follow, adjust, or handle in a different manner as long as +[our conduct is consistent](#uphold-code-of-conduct) + +### Contribution is stuck + +A contribution can get stuck often due to lack of bandwidth and language barrier. For bandwidth issues, +check whether the author needs help. Make sure you get their permission before pushing code into their existing PR - +do not create a new PR unless strictly necessary. + +For language barrier and others, offer a 1:1 chat to get them unblocked. Often times, English might not be their +primary language, and writing in public might put them off, or come across not the way they intended to be. + +In other cases, you may have constrained capacity. Use `help wanted` label when you want to signal other maintainers +and external contributors that you could use a hand to move it forward. + +### Insufficient feedback or information + +When in doubt, use the `need-more-information` label to signal more context and feedback are necessary before proceeding. + +### Crediting contributions + +We credit all contributions as part of each [release note](https://github.com/aws-powertools/powertools-lambda-java/releases){target="_blank"} +as an automated process. If you find contributors are missing from the release note you're producing, please add them manually. + +### Is that a bug? + +A bug produces incorrect or unexpected results at runtime that differ from its intended behavior. +Bugs must be reproducible. They directly affect customers experience at runtime despite following its recommended usage. + +Documentation snippets, use of internal components, or unadvertised functionalities are not considered bugs. + +### Mentoring contributions + +Always favor mentoring issue authors to contribute, unless they're not interested or the implementation is sensitive (_e.g., complexity, time to release, etc._). + +Make use of `help wanted` and `good first issue` to signal additional contributions the community can help. + +### Long running issues or PRs + +Try offering a 1:1 call in the attempt to get to a mutual understanding and clarify areas that maintainers could help. + +In the rare cases where both parties don't have the bandwidth or expertise to continue, it's best to use the `revisit-in-3-months` label. By then, see if it's possible to break the PR or issue in smaller chunks, and eventually close if there is no progress. diff --git a/mkdocs.yml b/mkdocs.yml index da00b24d1..841a95fae 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,6 +18,8 @@ nav: - utilities/validation.md - utilities/custom_resources.md - utilities/serialization.md + - Processes: + - processes/maintainers.md theme: name: material From bc40ea251eaba7320c2f5ef1e2c4883d4c35e02d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:58:07 +0200 Subject: [PATCH 0404/1008] build(deps): bump aws.sdk.version from 2.20.114 to 2.20.115 (#1328) Bumps `aws.sdk.version` from 2.20.114 to 2.20.115. Updates `software.amazon.awssdk:bom` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:http-client-spi` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:url-connection-client` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:s3` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:lambda` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:cloudwatch` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:xray` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:cloudformation` from 2.20.114 to 2.20.115 Updates `software.amazon.awssdk:sts` from 2.20.114 to 2.20.115 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 2de8c75c4..5d6d3a4ad 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.114</aws.sdk.version> + <aws.sdk.version>2.20.115</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 98f7fc9c2..13cb039bd 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.114</version> + <version>2.20.115</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index b0f7030ec..9ed8dbdd2 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <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.114</aws.sdk.version> + <aws.sdk.version>2.20.115</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From e378d4dd4e7e852656995a4dcafc23aedafbfc0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:03:50 +0200 Subject: [PATCH 0405/1008] build(deps): bump com.puppycrawl.tools:checkstyle (#1329) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.12.1 to 10.12.2. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.1...checkstyle-10.12.2) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ed8dbdd2..cab0a3574 100644 --- a/pom.xml +++ b/pom.xml @@ -564,7 +564,7 @@ <dependency> <groupId>com.puppycrawl.tools</groupId> <artifactId>checkstyle</artifactId> - <version>10.12.1</version> + <version>10.12.2</version> </dependency> </dependencies> <executions> From e2ef948a08eb64887d8dd09f60cda29985fa0b8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:50:34 +0200 Subject: [PATCH 0406/1008] build(deps): bump aws.sdk.version from 2.20.115 to 2.20.116 (#1331) Bumps `aws.sdk.version` from 2.20.115 to 2.20.116. Updates `software.amazon.awssdk:bom` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:http-client-spi` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:url-connection-client` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:s3` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:lambda` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:cloudwatch` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:xray` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:cloudformation` from 2.20.115 to 2.20.116 Updates `software.amazon.awssdk:sts` from 2.20.115 to 2.20.116 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 5d6d3a4ad..20e1b4581 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.115</aws.sdk.version> + <aws.sdk.version>2.20.116</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 13cb039bd..e1a4b659e 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.115</version> + <version>2.20.116</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index cab0a3574..1d80b3081 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <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.115</aws.sdk.version> + <aws.sdk.version>2.20.116</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 3a106ca7af82c1b548789d8584d7ea9b52b8959c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 20:06:49 +0200 Subject: [PATCH 0407/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1330) --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 3e7f0f460..7e354d15e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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.88.0</cdk.version> + <cdk.version>2.89.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From a05823bc45407704bda69272b483842d98190a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:53:24 +0200 Subject: [PATCH 0408/1008] feat: large message in SQS and SNS (#1310) --- .github/workflows/run-e2e-tests.yml | 7 + docs/utilities/large_messages.md | 419 ++++++++ docs/utilities/sqs_large_message_handling.md | 121 ++- mkdocs.yml | 2 +- pom.xml | 28 +- .../handlers/idempotency/pom.xml | 12 + .../lambda/powertools/e2e/Function.java | 2 + .../idempotency/src/main/resources/log4j2.xml | 16 + .../handlers/largemessage/pom.xml | 72 ++ .../lambda/powertools/e2e/Function.java | 77 ++ .../src/main/resources/log4j2.xml | 16 + .../handlers/largemessage_idempotent/pom.xml | 77 ++ .../lambda/powertools/e2e/Function.java | 105 ++ .../src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/pom.xml | 20 + powertools-e2e-tests/pom.xml | 28 + .../lambda/powertools/IdempotencyE2ET.java | 7 +- .../lambda/powertools/LargeMessageE2ET.java | 167 +++ .../LargeMessageIdempotentE2ET.java | 191 ++++ .../amazon/lambda/powertools/LoggingE2ET.java | 5 +- .../amazon/lambda/powertools/MetricsE2ET.java | 5 +- .../lambda/powertools/ParametersE2ET.java | 4 +- .../amazon/lambda/powertools/TracingE2ET.java | 4 +- .../powertools/testutils/Infrastructure.java | 89 +- .../src/test/resources/large_sqs_message.txt | 977 ++++++++++++++++++ powertools-idempotency/pom.xml | 1 - .../internal/IdempotentAspect.java | 3 + powertools-large-messages/pom.xml | 166 +++ .../largemessages/LargeMessage.java | 70 ++ .../largemessages/LargeMessageConfig.java | 83 ++ .../LargeMessageProcessingException.java | 28 + .../internal/LargeMessageAspect.java | 61 ++ .../internal/LargeMessageProcessor.java | 142 +++ .../LargeMessageProcessorFactory.java | 36 + .../internal/LargeSNSMessageProcessor.java | 52 + .../internal/LargeSQSMessageProcessor.java | 173 ++++ .../largemessages/LargeMessageConfigTest.java | 57 + .../internal/LargeMessageAspectTest.java | 333 ++++++ .../LargeMessageProcessorFactoryTest.java | 46 + powertools-sqs/pom.xml | 1 + .../powertools/sqs/SqsLargeMessage.java | 8 +- .../lambda/powertools/sqs/SqsUtils.java | 4 + spotbugs-exclude.xml | 8 + 43 files changed, 3650 insertions(+), 89 deletions(-) create mode 100644 docs/utilities/large_messages.md create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/largemessage/pom.xml create mode 100644 powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/largemessage/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml create mode 100644 powertools-e2e-tests/handlers/largemessage_idempotent/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/largemessage_idempotent/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java create mode 100644 powertools-e2e-tests/src/test/resources/large_sqs_message.txt create mode 100644 powertools-large-messages/pom.xml create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfig.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageProcessingException.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSNSMessageProcessor.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java create mode 100644 powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfigTest.java create mode 100644 powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java create mode 100644 powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactoryTest.java diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 4ed79ca80..a998e6b2d 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -14,9 +14,16 @@ on: - '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 diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md new file mode 100644 index 000000000..c4947a6e8 --- /dev/null +++ b/docs/utilities/large_messages.md @@ -0,0 +1,419 @@ +--- +title: Large Messages +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). + +## Features + +- Automatically retrieve the content of S3 objects when SQS or SNS messages have been offloaded to S3. +- Automatically delete the S3 Objects after processing succeeds. +- Compatible with the batch module (with SQS). + +## Background + +```mermaid +stateDiagram-v2 + direction LR + Function : Lambda Function + + state Application { + direction TB + sendMsg: sendMessage(QueueUrl, MessageBody) + extendLib: extended-client-lib + [*] --> sendMsg + sendMsg --> extendLib + state extendLib { + state if_big <<choice>> + bigMsg: MessageBody > 256KB ? + 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 + putObject --> updateMsg + updateMsg --> [*] + } + } + + state Function { + direction TB + iterateMsgs: Iterate over messages + ptLargeMsg: powertools-large-messages + [*] --> Handler + Handler --> iterateMsgs + iterateMsgs --> ptLargeMsg + state ptLargeMsg { + state if_pointer <<choice>> + pointer: Message attribute <br>for large message ? + normalMsg: Small message,<br>body left unchanged + getObject: getObject(S3Pointer) + deleteObject: deleteObject(S3Pointer) + updateBody: Update message body<br>with content from S3 object<br>and remove message attribute + updateMD5: Update MD5 of the body<br>and attributes (SQS only) + yourcode: <b>YOUR CODE HERE!</b> + pointer --> if_pointer + if_pointer --> normalMsg : False + normalMsg --> [*] + if_pointer --> getObject : True + getObject --> updateBody + updateBody --> updateMD5 + updateMD5 --> yourcode + yourcode --> deleteObject + deleteObject --> [*] + } + } + + [*] --> Application + Application --> Function : Lambda Invocation + Function --> [*] + +``` + +SQS and SNS message payload is limited to 256KB. 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 +([SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-s3-messages.html) +/ [SNS](https://docs.aws.amazon.com/sns/latest/dg/large-message-payloads.html)) + +When offloaded to S3, the message contains a specific message attribute and the payload only contains a pointer to the +S3 object (bucket and object key). + +This utility automatically retrieves messages which have been offloaded to S3 using the +extended client libraries. Once a message's payload has been processed successfully, the +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. + +## 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" + + ```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>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> + </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-large-messages:{{ 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-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 +on the S3 bucket used for the large messages offloading: + +- `s3:GetObject` +- `s3:DeleteObject` + +## Annotation + +The annotation `@LargeMessage` can be used on any method where the *first* parameter is one of: + +- `SQSEvent.SQSMessage` +- `SNSEvent.SNSRecord` + +=== "SQS Example" + + ```java hl_lines="8 13 15" + import software.amazon.lambda.powertools.largemessages.LargeMessage; + + public class SqsMessageHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + @Override + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + for (SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @LargeMessage + private void processRawMessage(SQSEvent.SQSMessage sqsMessage, Context context) { + // sqsMessage.getBody() will contain the content of the S3 object + } + } + ``` + +=== "SNS Example" + + ```java hl_lines="7 11 13" + import software.amazon.lambda.powertools.largemessages.LargeMessage; + + public class SnsRecordHandler implements RequestHandler<SNSEvent, String> { + + @Override + public String handleRequest(SNSEvent event, Context context) { + processSNSRecord(event.records.get(0)); // there are always only one message + return "Hello World"; + } + + @LargeMessage + private void processSNSRecord(SNSEvent.SNSRecord snsRecord) { + // snsRecord.getSNS().getMessage() will contain the content of the S3 object + } + } + ``` + +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. + +If this is the case, the utility will retrieve the object from S3 using the `getObject(bucket, key)` API, +and place the content of the object in the message payload. You can then directly use the content of the message. +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: + +=== "Don't delete S3 Objects" + ```java + @LargeMessage(deleteS3Object = false) + private void processRawMessage(SQSEvent.SQSMessage sqsMessage) { + // do something with the message + } + ``` + +!!! 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); + } + + @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 + } + } + ``` + +!!! 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. + + ```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(); + } + + @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 + } + } + ``` + +## Customizing S3 client configuration + +To interact with S3, the utility creates a default S3 Client : + +=== "Default S3 Client" + ```java + S3Client client = S3Client.builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv(AWS_REGION_ENV))) + .build(); + ``` + +If you need to customize this `S3Client`, you can leverage the `LargeMessageConfig` singleton: + +=== "Custom S3 Client" + ```java hl_lines="6" + import software.amazon.lambda.powertools.largemessages.LargeMessage; + + 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) { + processSNSRecord(event.records.get(0)); + return "Hello World"; + } + + @LargeMessage + private void processSNSRecord(SNSEvent.SNSRecord snsRecord) { + // snsRecord.getSNS().getMessage() will contain the content of the S3 object + } + } + ``` + +## Migration from the SQS Large Message utility + +- 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. + 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 diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md index 82e1afef4..6308f1c79 100644 --- a/docs/utilities/sqs_large_message_handling.md +++ b/docs/utilities/sqs_large_message_handling.md @@ -3,6 +3,10 @@ title: SQS Large Message Handling description: Utility --- +!!! warning +This module is now deprecated and will be removed in version 2. +See [Large Message Handling](large_messages.md) 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. @@ -11,16 +15,19 @@ The utility automatically retrieves messages which have been offloaded to S3 usi 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. +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> - ``` + +```xml + +<dependency> + <groupId>com.amazonaws</groupId> + <artifactId>amazon-sqs-java-extended-client-lib</artifactId> + <version>1.1.0</version> +</dependency> +``` === "Gradle" ```groovy @@ -33,48 +40,49 @@ This utility is compatible with versions *[1.1.0+](https://github.com/awslabs/am 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> - ``` + +```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" @@ -186,12 +194,13 @@ which implements `com.amazonaws.services.lambda.runtime.RequestHandler` with `@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. - +!!! 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)` diff --git a/mkdocs.yml b/mkdocs.yml index 841a95fae..62d8d75ce 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,7 +13,7 @@ nav: - Utilities: - utilities/idempotency.md - utilities/parameters.md - - utilities/sqs_large_message_handling.md + - utilities/large_messages.md - utilities/batch.md - utilities/validation.md - utilities/custom_resources.md diff --git a/pom.xml b/pom.xml index 1d80b3081..3e751f186 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ <module>powertools-test-suite</module> <module>powertools-cloudformation</module> <module>powertools-idempotency</module> + <module>powertools-large-messages</module> <module>powertools-e2e-tests</module> <module>examples</module> </modules> @@ -89,7 +90,7 @@ <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-jupiter.version>5.9.3</junit-jupiter.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> </properties> @@ -243,21 +244,16 @@ <!-- Test dependencies --> <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <version>${junit-jupiter.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <version>${junit-jupiter.version}</version> - <scope>test</scope> + <groupId>org.junit</groupId> + <artifactId>junit-bom</artifactId> + <version>${junit.version}</version> + <type>pom</type> + <scope>import</scope> </dependency> <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-params</artifactId> - <version>${junit-jupiter.version}</version> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> + <version>1.9.1</version> <scope>test</scope> </dependency> <dependency> @@ -540,9 +536,9 @@ </build> </profile> <profile> - <id>jdk11</id> + <id>newerThanJdk8</id> <activation> - <jdk>11</jdk> + <jdk>[9,)</jdk> </activation> <build> <plugins> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 4e24c738c..25dfbfabf 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -17,6 +17,14 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> @@ -38,6 +46,10 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> </aspectLibraries> </configuration> <executions> 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 8c2b5fc58..e4c2f2b9a 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 @@ -28,6 +28,7 @@ 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.logging.Logging; public class Function implements RequestHandler<Input, String> { @@ -53,6 +54,7 @@ public Function(DynamoDbClient client) { ).configure(); } + @Logging(logEvent = true) @Idempotent public String handleRequest(Input input, Context context) { DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/idempotency/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency/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 new file mode 100644 index 000000000..c626f5f64 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -0,0 +1,72 @@ +<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>1.0.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> + + <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</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> + </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-large-messages</artifactId> + </aspectLibrary> + <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> +</project> diff --git a/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..70f1bceea --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.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.e2e; + +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.LargeMessage; +import software.amazon.lambda.powertools.logging.Logging; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +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(); + } + } + + @Logging(logEvent = true) + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + for (SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @LargeMessage + 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()); + } + + private String md5(String message) { + return BinaryUtils.toHex(Md5Utils.computeMD5Hash(message.getBytes(StandardCharsets.UTF_8))); + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/largemessage/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/largemessage/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage/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_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml new file mode 100644 index 000000000..9635efd87 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -0,0 +1,77 @@ +<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>1.0.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> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-large-messages</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>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</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-idempotency</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-large-messages</artifactId> + </aspectLibrary> + <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> +</project> 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 new file mode 100644 index 000000000..b9f737857 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/java/software/amazon/lambda/powertools/e2e/Function.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.e2e; + +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.nio.charset.StandardCharsets; +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 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.idempotency.Idempotency; +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.largemessages.LargeMessage; +import software.amazon.lambda.powertools.logging.Logging; + +public class Function implements RequestHandler<SQSEvent, SQSBatchResponse> { + + private static final String TABLE_FOR_ASYNC_TESTS = System.getenv("TABLE_FOR_ASYNC_TESTS"); + private final DynamoDbClient client; + + public Function() { + this(DynamoDbClient + .builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build()); + } + + public Function(DynamoDbClient client) { + this.client = client; + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withExpiration(Duration.of(22, ChronoUnit.SECONDS)) + .withEventKeyJMESPath("body") // get the body of the message + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build() + ).configure(); + } + + @Logging(logEvent = true) + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + for (SQSEvent.SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @Idempotent + @LargeMessage(deleteS3Object = false) + private String processRawMessage(@IdempotencyKey SQSEvent.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)); + } + + Instant now = Instant.now(); + 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("now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).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()); + + DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + return dtf.format(now); + } + + private String md5(String message) { + return BinaryUtils.toHex(Md5Utils.computeMD5Hash(message.getBytes(StandardCharsets.UTF_8))); + } +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ae26364dd..4dd8cbb45 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -20,6 +20,8 @@ <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> </properties> <modules> @@ -32,6 +34,14 @@ <dependencyManagement> <dependencies> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>bom</artifactId> + <version>${aws.sdk.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> @@ -57,6 +67,11 @@ <artifactId>powertools-parameters</artifactId> <version>${lambda.powertools.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-large-messages</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> @@ -67,6 +82,11 @@ <artifactId>aws-lambda-java-events</artifactId> <version>${lambda.java.events}</version> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <version>${log4j.version}</version> + </dependency> </dependencies> </dependencyManagement> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 7e354d15e..500b7f30a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -51,6 +51,13 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>dynamodb</artifactId> + <version>${aws.sdk.version}</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>cloudwatch</artifactId> @@ -65,6 +72,20 @@ <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> + <scope>test</scope> + </dependency> + <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> @@ -77,6 +98,12 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.11.0</version> + </dependency> + <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> @@ -182,6 +209,7 @@ </execution> </executions> <configuration> + <skipAfterFailureCount>1</skipAfterFailureCount> <!-- no need to continue / deploy more resources and lose time --> <includes> <include>**/*E2ET.java</include> </includes> 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 fed823299..242d1a2db 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 @@ -14,10 +14,11 @@ package software.amazon.lambda.powertools; +import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.time.Year; -import java.util.Collections; +import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; @@ -40,9 +41,9 @@ public static void setup() { .testName(IdempotencyE2ET.class.getSimpleName()) .pathToFunction("idempotency") .idempotencyTable("idempo" + random) - .environmentVariables(Collections.singletonMap("IDEMPOTENCY_TABLE", "idempo" + random)) .build(); - functionName = infrastructure.deploy(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); } @AfterAll 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 new file mode 100644 index 000000000..2d9f74135 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java @@ -0,0 +1,167 @@ +package software.amazon.lambda.powertools; + +import com.amazon.sqs.javamessaging.AmazonSQSExtendedClient; +import com.amazon.sqs.javamessaging.ExtendedClientConfiguration; +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.Timeout; +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; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.QueryRequest; +import software.amazon.awssdk.services.dynamodb.model.QueryResponse; +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.Infrastructure; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; + +public class LargeMessageE2ET { + + private static final Logger LOG = LoggerFactory.getLogger(LargeMessageE2ET.class); + 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 final S3Client s3Client = S3Client.builder() + .httpClient(httpClient) + .region(region) + .build(); + private static final DynamoDbClient dynamoDbClient = DynamoDbClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + + private static Infrastructure infrastructure; + private static String functionName; + private static String bucketName; + private static String queueUrl; + private static String tableName; + private String messageId; + + @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()) + .queue(queueName) + .largeMessagesBucket(bucketName) + .pathToFunction("largemessage") + .timeoutInSeconds(60) + .build(); + + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); + queueUrl = outputs.get("QueueURL"); + tableName = outputs.get("TableNameForAsyncTests"); + + LOG.info("Testing '" + LargeMessageE2ET.class.getSimpleName() + "'"); + } + + @AfterEach + public void reset() { + if (messageId != null) { + Map<String, AttributeValue> itemToDelete = new HashMap<>(); + itemToDelete.put("functionName", AttributeValue.builder().s(functionName).build()); + itemToDelete.put("id", AttributeValue.builder().s(messageId).build()); + dynamoDbClient.deleteItem(DeleteItemRequest.builder().tableName(tableName).key(itemToDelete).build()); + messageId = null; + } + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) + infrastructure.destroy(); + } + + @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 + + // 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(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"); + } +} 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 new file mode 100644 index 000000000..986d22a2f --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java @@ -0,0 +1,191 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +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; +import org.junit.jupiter.api.Test; +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; +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.QueryRequest; +import software.amazon.awssdk.services.dynamodb.model.QueryResponse; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +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.Infrastructure; + +public class LargeMessageIdempotentE2ET { + + private static final Logger LOG = LoggerFactory.getLogger(LargeMessageIdempotentE2ET.class); + 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 final S3Client s3Client = S3Client.builder() + .httpClient(httpClient) + .region(region) + .build(); + // cannot use the extended library as it will create different S3 objects (we need to have the same for Idempotency) + private static final SqsClient sqsClient = SqsClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + private static final DynamoDbClient dynamoDbClient = DynamoDbClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + + private static Infrastructure infrastructure; + private static String functionName; + private static String bucketName; + private static String queueUrl; + private static String tableName; + + + @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(LargeMessageIdempotentE2ET.class.getSimpleName()) + .pathToFunction("largemessage_idempotent") + .idempotencyTable("idempo" + random) + .queue(queueName) + .largeMessagesBucket(bucketName) + .build(); + + Map<String, String> outputs = infrastructure.deploy(); + + functionName = outputs.get(Infrastructure.FUNCTION_NAME_OUTPUT); + queueUrl = outputs.get("QueueURL"); + tableName = outputs.get("TableNameForAsyncTests"); + + LOG.info("Testing '" + LargeMessageIdempotentE2ET.class.getSimpleName() + "'"); + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + + @Test + public 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)); + + // WHEN + SendMessageRequest messageRequest = SendMessageRequest.builder() + .queueUrl(queueUrl) + .messageBody(String.format( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"%s\",\"s3Key\":\"%s\"}]", + bucketName, key)) + .messageAttributes(Collections.singletonMap("SQSLargePayloadSize", MessageAttributeValue.builder() + .stringValue("300977") + .dataType("Number") + .build())) + .build(); + + // 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 + .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); + assertThat(Integer.valueOf(items.get(0).get("bodySize").n())).isEqualTo(300977); + assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); + long timeOfInvocation1 = Long.parseLong(items.get(0).get("now").n()); + + // WHEN + // Second invocation + // send the same message before ttl expires + sqsClient.sendMessage(messageRequest); + Thread.sleep(waitMs); // wait for the function to be invoked & executed + + // THEN + response = dynamoDbClient.query(request); + items = response.items(); + assertThat(items).hasSize(1); // we should have the same number of items (idempotency working) + assertThat(Integer.valueOf(items.get(0).get("bodySize").n())).isEqualTo(300977); + assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); + long timeOfInvocation2 = Long.parseLong(items.get(0).get("now").n()); + assertThat(timeOfInvocation2).isEqualTo(timeOfInvocation1); // should be the same as first invocation + + // WHEN + // waiting for TTL to expire + Thread.sleep(24000); + + // Third invocation + // send the same message again + sqsClient.sendMessage(messageRequest); + Thread.sleep(waitMs); // wait for the function to be invoked + + // THEN + response = dynamoDbClient.query(request); + items = response.items(); + assertThat(items).hasSize(2); // not idempotent anymore, function should put a new item in DDB + assertThat(Integer.valueOf(items.get(0).get("bodySize").n())).isEqualTo(300977); + assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); + assertThat(Integer.valueOf(items.get(1).get("bodySize").n())).isEqualTo(300977); + assertThat(items.get(1).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); + long timeOfInvocation3 = Long.parseLong(items.get(0).get("now").n()); + long timeOfInvocation4 = Long.parseLong(items.get(1).get("now").n()); + assertThat(timeOfInvocation3).isNotEqualTo(timeOfInvocation4); // should be different (not idempotent anymore) + + } +} 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 e4b8dccd2..f958970d8 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 @@ -15,12 +15,14 @@ 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 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; @@ -52,7 +54,8 @@ public static void setup() { }) .collect(Collectors.toMap(data -> data[0], data -> data[1]))) .build(); - functionName = infrastructure.deploy(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); } @AfterAll 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 32965b3be..80673b995 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 @@ -15,10 +15,12 @@ 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.util.Collections; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -50,7 +52,8 @@ public static void setup() { }) .collect(Collectors.toMap(data -> data[0], data -> data[1]))) .build(); - functionName = infrastructure.deploy(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); } @AfterAll 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 678321a99..9582f9f23 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 @@ -15,6 +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 static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.util.HashMap; @@ -60,7 +61,8 @@ public void setup() { }) .collect(Collectors.toMap(data -> data[0], data -> data[1]))) .build(); - functionName = infrastructure.deploy(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); } @AfterAll 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 043de5494..0827d91ae 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,6 +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 static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.util.Collections; @@ -46,7 +47,8 @@ public static void setup() { .tracing(true) .environmentVariables(Collections.singletonMap("POWERTOOLS_SERVICE_NAME", service)) .build(); - functionName = infrastructure.deploy(); + Map<String, String> outputs = infrastructure.deploy(); + functionName = outputs.get(FUNCTION_NAME_OUTPUT); } @AfterAll 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 62bb018f4..996f49bd4 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 @@ -33,6 +33,7 @@ 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.Duration; import software.amazon.awscdk.RemovalPolicy; @@ -52,9 +53,14 @@ import software.amazon.awscdk.services.lambda.Code; import software.amazon.awscdk.services.lambda.Function; import software.amazon.awscdk.services.lambda.Tracing; +import software.amazon.awscdk.services.lambda.eventsources.SqsEventSource; import software.amazon.awscdk.services.logs.LogGroup; import software.amazon.awscdk.services.logs.RetentionDays; +import software.amazon.awscdk.services.s3.Bucket; +import software.amazon.awscdk.services.s3.LifecycleRule; import software.amazon.awscdk.services.s3.assets.AssetOptions; +import software.amazon.awscdk.services.sqs.DeadLetterQueue; +import software.amazon.awscdk.services.sqs.Queue; import software.amazon.awssdk.core.waiters.WaiterResponse; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -85,6 +91,7 @@ * and the CloudFormation stack is created (with the SDK `createStack`) */ public class Infrastructure { + public static final String FUNCTION_NAME_OUTPUT = "functionName"; private static final Logger LOG = LoggerFactory.getLogger(Infrastructure.class); private final String stackName; @@ -102,6 +109,9 @@ public class Infrastructure { private final String idempotencyTable; private final AppConfig appConfig; private final SdkHttpClient httpClient; + private final String queue; + private final String largeMessagesBucket; + private String functionName; private Object cfnTemplate; private String cfnAssetDirectory; @@ -115,6 +125,8 @@ private Infrastructure(Builder builder) { this.pathToFunction = builder.pathToFunction; this.idempotencyTable = builder.idemPotencyTable; this.appConfig = builder.appConfig; + this.queue = builder.queue; + this.largeMessagesBucket = builder.largeMessagesBucket; this.app = new App(); this.stack = createStackWithLambda(); @@ -147,7 +159,7 @@ public static Builder builder() { * * @return the name of the function deployed part of the stack */ - public String deploy() { + public Map<String, String> deploy() { uploadAssets(); LOG.info("Deploying '" + stackName + "' on account " + account); cfn.createStack(CreateStackRequest.builder() @@ -160,12 +172,14 @@ public String deploy() { WaiterResponse<DescribeStacksResponse> waiterResponse = cfn.waiter().waitUntilStackCreateComplete(DescribeStacksRequest.builder().stackName(stackName).build()); if (waiterResponse.matched().response().isPresent()) { - LOG.info("Stack " + waiterResponse.matched().response().get().stacks().get(0).stackName() + - " successfully deployed"); + 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())); + return outputs; } else { throw new RuntimeException("Failed to create stack"); } - return functionName; } /** @@ -182,6 +196,7 @@ public void destroy() { * @return the CDK stack */ private Stack createStackWithLambda() { + boolean createTableForAsyncTests = false; Stack stack = new Stack(app, stackName); List<String> packagingInstruction = Arrays.asList( "/bin/sh", @@ -209,6 +224,9 @@ private Stack createStackWithLambda() { .outputType(BundlingOutput.ARCHIVED); functionName = stackName + "-function"; + CfnOutput.Builder.create(stack, FUNCTION_NAME_OUTPUT) + .value(functionName) + .build(); LOG.debug("Building Lambda function with command " + packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); @@ -244,10 +262,43 @@ private Stack createStackWithLambda() { .tableName(idempotencyTable) .timeToLiveAttribute("expiration") .build(); + function.addEnvironment("IDEMPOTENCY_TABLE", idempotencyTable); table.grantReadWriteData(function); } + if (!StringUtils.isEmpty(queue)) { + Queue sqsQueue = Queue.Builder + .create(stack, "SQSQueue") + .queueName(queue) + .visibilityTimeout(Duration.seconds(timeout * 6)) + .retentionPeriod(Duration.seconds(timeout * 6)) + .build(); + DeadLetterQueue.builder() + .queue(sqsQueue) + .maxReceiveCount(1) // do not retry in case of error + .build(); + sqsQueue.grantConsumeMessages(function); + SqsEventSource sqsEventSource = SqsEventSource.Builder.create(sqsQueue).enabled(true).batchSize(1).build(); + function.addEventSource(sqsEventSource); + CfnOutput.Builder + .create(stack, "QueueURL") + .value(sqsQueue.getQueueUrl()) + .build(); + createTableForAsyncTests = true; + } + + if (!StringUtils.isEmpty(largeMessagesBucket)) { + Bucket offloadBucket = Bucket.Builder + .create(stack, "LargeMessagesOffloadBucket") + .removalPolicy(RemovalPolicy.RETAIN) // autodelete does not work without cdk deploy + .bucketName(largeMessagesBucket) + .build(); + // instead of autodelete, have a lifecycle rule to delete files after a day + LifecycleRule.builder().expiration(Duration.days(1)).enabled(true).build(); + offloadBucket.grantReadWrite(function); + } + if (appConfig != null) { CfnApplication app = CfnApplication.Builder .create(stack, "AppConfigApp") @@ -260,7 +311,7 @@ private Stack createStackWithLambda() { .name(appConfig.getEnvironment()) .build(); - // Create a fast deployment strategy so we don't have to wait ages + // Create a fast deployment strategy, so we don't have to wait ages CfnDeploymentStrategy fastDeployment = CfnDeploymentStrategy.Builder .create(stack, "AppConfigDeployment") .name("FastDeploymentStrategy") @@ -306,11 +357,25 @@ private Stack createStackWithLambda() { // We need to chain the deployments to keep CFN happy if (previousDeployment != null) { - deployment.addDependsOn(previousDeployment); + deployment.addDependency(previousDeployment); } previousDeployment = deployment; } } + if (createTableForAsyncTests) { + Table table = Table.Builder + .create(stack, "TableForAsyncTests") + .billingMode(BillingMode.PAY_PER_REQUEST) + .removalPolicy(RemovalPolicy.DESTROY) + .partitionKey(Attribute.builder().name("functionName").type(AttributeType.STRING).build()) + .sortKey(Attribute.builder().name("id").type(AttributeType.STRING).build()) + .build(); + + table.grantReadWriteData(function); + function.addEnvironment("TABLE_FOR_ASYNC_TESTS", table.getTableName()); + CfnOutput.Builder.create(stack, "TableNameForAsyncTests").value(table.getTableName()).build(); + } + return stack; } @@ -379,11 +444,13 @@ public static class Builder { public String pathToFunction; public String testName; public AppConfig appConfig; + private String largeMessagesBucket; private String stackName; private boolean tracing = false; private JavaRuntime runtime; private Map<String, String> environmentVariables = new HashMap<>(); private String idemPotencyTable; + private String queue; private Builder() { getJavaRuntime(); @@ -453,6 +520,16 @@ public Builder timeoutInSeconds(long timeoutInSeconds) { this.timeoutInSeconds = timeoutInSeconds; return this; } + + public Builder queue(String queue) { + this.queue = queue; + return this; + } + + public Builder largeMessagesBucket(String largeMessagesBucket) { + this.largeMessagesBucket = largeMessagesBucket; + return this; + } } private static class Asset { diff --git a/powertools-e2e-tests/src/test/resources/large_sqs_message.txt b/powertools-e2e-tests/src/test/resources/large_sqs_message.txt new file mode 100644 index 000000000..102216e83 --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/large_sqs_message.txt @@ -0,0 +1,977 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in nibh eget ante viverra mattis. Etiam elit est, pharetra efficitur fermentum at, accumsan id eros. Aenean accumsan nisi facilisis tempor suscipit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam magna erat, tincidunt eu placerat at, molestie at sem. In sit amet cursus mauris. Etiam ullamcorper lacus nisi, et porta metus semper id. Nulla mauris nunc, pharetra at facilisis a, consequat id metus. Sed convallis ligula non felis vulputate finibus. Curabitur nisl nulla, pharetra in justo a, dapibus ultricies dui. Morbi ultricies sollicitudin purus, quis pellentesque leo rhoncus sed. Curabitur sed eros quis lacus vulputate pretium. Vestibulum vitae urna nec arcu vulputate efficitur. Cras venenatis sodales dolor non ullamcorper. + +Donec eu placerat magna. Vestibulum justo lacus, luctus ac luctus sit amet, dapibus at orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec eu tortor ac justo pharetra hendrerit. Phasellus ornare lacinia vestibulum. Maecenas diam orci, molestie rhoncus fringilla vitae, aliquam eu purus. Maecenas vehicula felis dui, vel dapibus odio lobortis quis. Cras a velit non nisl efficitur tempor. + +Suspendisse magna enim, ultrices et elementum ut, venenatis ut purus. Curabitur convallis vel tortor id rhoncus. Pellentesque varius arcu non imperdiet eleifend. Vestibulum fringilla aliquet vehicula. Nullam et lorem ullamcorper, tincidunt ante eget, egestas ex. Donec laoreet, justo id tincidunt egestas, lectus diam faucibus tortor, nec venenatis felis leo a urna. In at tortor semper augue finibus ornare et vitae erat. Praesent vulputate, dui sed pulvinar porta, justo tortor feugiat tellus, eget accumsan velit turpis at orci. Nulla vitae velit facilisis augue sagittis fringilla nec sagittis nunc. + +Duis vel elementum odio, id eleifend mauris. Nam in ultrices nulla. Quisque pharetra blandit risus eget lacinia. Nulla mattis mi felis, a ultrices nisi egestas vulputate. Donec a augue ac ex laoreet semper at porta ante. Vestibulum arcu ipsum, vehicula vel mollis lobortis, ullamcorper sed augue. Ut viverra faucibus nunc, sit amet sodales diam gravida sollicitudin. Fusce finibus quam sed ornare consequat. Suspendisse viverra ultricies dui in volutpat. Maecenas blandit erat in rhoncus placerat. Phasellus eu nisi rutrum, bibendum nunc eu, viverra ex. Praesent sollicitudin mi sit amet dictum convallis. Vivamus purus ligula, interdum at tincidunt sit amet, pellentesque ut elit. Aenean vitae ultrices ex. In facilisis lectus est, nec vulputate diam tristique vel. In gravida tincidunt ex et viverra. + +Pellentesque mi justo, dignissim a nisl quis, tempor dictum lacus. Nam tristique varius dolor tincidunt pharetra. Nullam iaculis est sed quam dignissim cursus. Duis sit amet vehicula nisi, sed consectetur velit. Nullam non tellus nec dolor venenatis porta quis at quam. In pharetra id orci sed faucibus. Cras et malesuada erat. + +Quisque fringilla commodo metus nec feugiat. Donec et est urna. Maecenas ut magna dui. Vivamus eu sem venenatis, porta mi at, efficitur tortor. In lacinia hendrerit elit, in faucibus nulla ultrices et. Mauris accumsan nec orci et vestibulum. Aliquam eget justo velit. Mauris fringilla ullamcorper enim, in elementum enim eleifend a. Nulla id libero ac arcu tristique ultrices. Maecenas mattis orci vitae nisl laoreet auctor. + +Quisque id aliquam orci. Nunc molestie vitae quam eu consectetur. Vivamus molestie venenatis est, nec lacinia odio faucibus eget. Etiam ornare eu leo eget posuere. Quisque elementum ligula at euismod placerat. Maecenas lobortis leo diam, vel egestas odio iaculis ac. Integer dapibus mi metus. Sed at eleifend ligula, ullamcorper vestibulum eros. Suspendisse dignissim metus in vulputate iaculis. Nam rhoncus felis sit amet velit scelerisque semper. Ut sed augue eget nulla laoreet scelerisque. + +Aenean convallis ipsum id turpis gravida, et elementum ante facilisis. Donec vitae arcu id mauris mollis hendrerit. Mauris imperdiet cursus nibh at laoreet. Sed sit amet enim magna. Mauris blandit nisi quis arcu sodales, eget consequat orci euismod. Curabitur id bibendum diam. Ut pharetra tellus nec enim tristique, id efficitur eros accumsan. Suspendisse potenti. Praesent vel porttitor risus. Nulla vitae ex nec ex hendrerit euismod non mattis arcu. Aenean eget velit massa. Pellentesque venenatis arcu mauris, sit amet scelerisque sem lobortis ornare. Vivamus auctor porta eros, eget blandit lorem semper non. Nunc sed nibh commodo, hendrerit nunc at, malesuada nisl. Aliquam pretium leo sed iaculis vehicula. Vivamus interdum porta augue. + +Suspendisse potenti. Aenean sodales vehicula erat, vel sollicitudin eros eleifend id. Suspendisse hendrerit malesuada est, imperdiet tristique ex aliquet ac. Vivamus ultricies placerat lorem, ac placerat lectus sollicitudin ac. Etiam consequat odio vel bibendum pretium. Integer elementum nisl magna. Nam et vehicula risus, sed mollis tortor. Ut in molestie turpis. Nam luctus, elit molestie varius luctus, lacus ligula ullamcorper elit, non interdum ipsum neque non nibh. Curabitur vulputate facilisis suscipit. Sed vitae augue eu ex luctus lacinia ac vitae quam. Quisque non nibh ex. Praesent gravida efficitur dui, a vulputate nulla ultrices vitae. Duis libero dolor, facilisis in tempus vitae, pharetra non libero. Sed urna leo, placerat quis neque at, interdum placerat odio. Interdum et malesuada fames ac ante ipsum primis in faucibus. + +Vivamus in hendrerit elit, quis egestas sem. Sed tempus dolor finibus massa ultrices, sed tristique quam semper. Pellentesque euismod molestie facilisis. Vivamus sit amet ultrices elit. Ut eleifend, libero eu molestie maximus, ipsum elit pulvinar tortor, et aliquet nulla orci eu est. Nullam pretium, ligula at faucibus gravida, velit mauris pulvinar felis, sit amet tincidunt magna dui ut quam. Phasellus porttitor eu nunc id maximus. Suspendisse volutpat suscipit porta. + +Integer dictum augue purus, sed cursus eros blandit id. Vestibulum non nibh viverra, molestie lectus eu, vestibulum justo. Nullam a libero non nibh dignissim posuere id vel orci. Nulla viverra lorem eget condimentum scelerisque. Donec porta nunc sed sapien pretium dapibus. In hac habitasse platea dictumst. Suspendisse sit amet ligula elementum, accumsan ipsum vel, imperdiet massa. Fusce scelerisque non erat ac bibendum. Pellentesque consequat vehicula euismod. Morbi sapien nisl, ultrices ut scelerisque consectetur, ornare et orci. Maecenas efficitur eros a venenatis rutrum. Aenean bibendum dui in enim luctus posuere. Donec placerat porta eros eu dignissim. Fusce nulla velit, sodales eget nibh gravida, tincidunt venenatis urna. Aenean condimentum, massa in fringilla lobortis, turpis felis lacinia metus, sit amet facilisis nisl quam aliquet diam. Praesent fringilla vitae arcu nec lobortis. + +In tincidunt nisi ac dictum cursus. Sed dolor purus, posuere vel dolor vel, semper tempor elit. Integer nec scelerisque tellus, sit amet dictum magna. Donec hendrerit aliquet libero, a varius velit dapibus quis. Donec lectus turpis, egestas vel vestibulum vitae, iaculis quis eros. Maecenas id convallis metus. Duis quis tellus iaculis, lobortis massa vitae, condimentum eros. Pellentesque scelerisque nisl id hendrerit tempor. Donec quam augue, maximus eu porta ut, venenatis a ante. Curabitur dictum et nibh sed auctor. Nam et consequat odio. In vitae magna a dui porta pellentesque quis id magna. Sed eu nunc bibendum, imperdiet nunc sit amet, cursus diam. Vivamus in odio vel nibh sollicitudin molestie. Morbi sit amet leo et odio congue pharetra. Aliquam quis suscipit orci. + +Donec at ornare orci. Suspendisse nec tellus elit. Nam erat urna, laoreet at elit sed, tristique interdum ligula. Nullam dapibus orci sed lorem convallis fringilla. Cras urna ipsum, tristique maximus nisl quis, auctor mattis quam. Donec finibus consectetur lectus et interdum. Aenean posuere lectus vel turpis auctor finibus. Praesent molestie lectus ipsum, et tristique quam porttitor ac. Suspendisse aliquam pretium tortor, id egestas erat. Nulla mollis, orci at semper vulputate, nisl est pharetra diam, sit amet vulputate diam augue a sem. Donec in mauris felis. + +Nunc eleifend at elit a volutpat. Integer semper quis quam at faucibus. Donec pretium purus vitae erat pretium posuere. Sed ac aliquet nulla. Nam ut sagittis mauris. Sed quis sagittis risus, id pretium urna. Nam sagittis elementum ornare. Aenean ligula sapien, congue eget mi quis, viverra commodo nibh. Suspendisse non iaculis magna, nec volutpat neque. Donec eu dapibus eros. + +Nam sit amet iaculis massa. Nullam vel laoreet tellus, id cursus tortor. In hac habitasse platea dictumst. Curabitur in urna risus. Proin dui sem, interdum accumsan cursus ut, dignissim in purus. Nunc vitae consequat arcu. Proin volutpat elementum quam in posuere. Pellentesque luctus arcu in ornare tempor. Cras est turpis, viverra vel consectetur ac, dictum a quam. Donec sagittis tortor sed volutpat accumsan. Curabitur a tellus vitae arcu pretium viverra vel in ligula. Pellentesque turpis augue, porta vitae quam id, fermentum congue leo. Sed nec fermentum turpis. Nulla vehicula vulputate sem eu consequat. In tincidunt nisi et mi maximus, non facilisis justo porttitor. Proin eu sapien aliquam, accumsan nunc non, pulvinar leo. + +Nam vitae commodo sem. Ut laoreet in quam in suscipit. Nullam at faucibus diam. Fusce ut purus at ex lobortis elementum vitae quis sapien. Nam tempus consectetur ex, sed luctus felis. Donec porttitor mi dui, non luctus quam consectetur eu. Ut a egestas diam. Etiam sodales, nisl vitae gravida pulvinar, libero est condimentum tellus, vitae ullamcorper tortor justo a urna. Maecenas ac velit accumsan, laoreet leo eget, elementum libero. Maecenas dictum tincidunt blandit. Proin sed bibendum urna. Phasellus fermentum tincidunt tempor. Mauris iaculis, mi ac fermentum interdum, nibh odio pellentesque nunc, vel scelerisque sapien sem id purus. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas imperdiet sit amet lectus at hendrerit. Vivamus luctus, elit non lacinia hendrerit, risus velit finibus nulla, quis sagittis ipsum diam ut odio. Sed mauris sapien, vehicula efficitur gravida sed, gravida in lectus. Fusce eget consectetur turpis, in fermentum neque. Nullam turpis turpis, feugiat a accumsan in, euismod a augue. Vestibulum nec neque libero. Phasellus eget efficitur turpis, fermentum molestie nunc. Sed consequat elit urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Nunc egestas consequat blandit. Donec quis porta sapien. Sed augue nunc, efficitur vitae tellus sed, finibus bibendum sem. Fusce id sem quis quam pharetra ultrices. Phasellus non convallis velit. Quisque erat tellus, pretium eget porta in, ornare a arcu. Aenean nec lectus lorem. Suspendisse dolor lectus, ullamcorper ornare lorem a, consequat lobortis elit. Nunc dignissim est nec gravida facilisis. Proin faucibus erat vel eros volutpat, in vulputate neque sodales. + +Nunc vitae imperdiet ipsum, faucibus vehicula magna. Nulla nec nisl sapien. Curabitur et dui eget tortor efficitur accumsan. Aenean in pellentesque erat. Nam condimentum neque pulvinar, aliquam nisl eu, mollis lorem. Integer rutrum arcu eget felis semper euismod. Quisque vestibulum vel diam a tempor. Aenean mollis, tellus sit amet pretium pulvinar, nulla dolor ornare ligula, eget dignissim orci tortor ut sapien. Nam ut ipsum id lectus venenatis tempus vel non ex. Suspendisse blandit vitae nunc vitae porta. Suspendisse tincidunt est sit amet ultricies consectetur. Nulla fermentum hendrerit ex, vehicula rhoncus arcu lobortis ut. Vestibulum fermentum ornare diam at pellentesque. Praesent nunc lorem, porta et magna nec, sodales commodo justo. Duis aliquam sapien et rutrum tempus. Vestibulum malesuada felis eu ligula posuere luctus. + +Maecenas lacinia, lectus eget rhoncus aliquam, tortor est gravida sapien, vel aliquam arcu erat et magna. Praesent fringilla leo eget neque posuere imperdiet. In porttitor elit non enim gravida euismod. Aliquam tempus, orci at interdum dapibus, mauris lacus egestas sapien, ac ullamcorper ex nibh sed orci. Quisque iaculis enim et lectus egestas, malesuada posuere lectus interdum. Sed dignissim neque vel turpis dictum ornare. Vestibulum suscipit consequat maximus. + +Ut et ante sit amet leo rutrum volutpat. Sed malesuada quis sapien et ornare. Aliquam ac ex enim. Curabitur vel quam et orci posuere feugiat. Pellentesque nec metus eget sapien eleifend tincidunt non sit amet arcu. Cras posuere metus eget risus varius fermentum. Nullam orci eros, efficitur nec sapien nec, pretium laoreet erat. Nam gravida purus mauris, ac viverra orci hendrerit sed. Aenean ligula massa, posuere id faucibus vitae, malesuada quis augue. Morbi consectetur mattis mi, quis sodales diam bibendum eget. Aliquam sagittis neque at feugiat posuere. Donec gravida lectus a lectus fringilla tincidunt. Vivamus volutpat dui et turpis condimentum, ut tincidunt tortor lacinia. Ut laoreet, ante in pellentesque sodales, elit mauris scelerisque dui, quis tincidunt quam massa ac enim. Nulla porta porta sapien vel sollicitudin. + +Phasellus sed lectus posuere, mollis ante non, feugiat odio. Aenean a quam id mi ornare dapibus. Donec venenatis ipsum non velit accumsan, id elementum dolor imperdiet. Phasellus lacinia erat diam, sit amet convallis lectus ornare in. Curabitur lorem ligula, maximus eu varius quis, auctor quis odio. Etiam in orci porttitor, sodales ipsum at, rhoncus turpis. Nulla eget luctus nisi. Duis convallis est eget ligula fringilla viverra. Duis nec sapien quis dui luctus ornare sit amet quis erat. Quisque nec justo dui. + +Sed eleifend, tellus quis laoreet rhoncus, leo turpis imperdiet turpis, pretium varius odio tortor et leo. Morbi ut mi fringilla, porttitor sem ac, accumsan ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum cursus dolor accumsan dolor aliquet dignissim. Duis sed feugiat erat. Donec sed arcu accumsan, ornare nisl eget, lobortis nibh. Praesent pulvinar quis justo et hendrerit. + +Sed velit nibh, efficitur ut leo sed, eleifend iaculis nisl. Mauris ac diam euismod, luctus enim maximus, tincidunt sem. Ut tempus magna vitae blandit bibendum. Sed sapien tortor, ultrices et justo vel, pharetra faucibus dui. Vivamus et vestibulum arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris tincidunt suscipit risus, vitae varius felis commodo in. Nullam semper tortor dolor, sit amet pharetra odio interdum hendrerit. Pellentesque sed justo eros. Cras quam purus, eleifend id tellus molestie, eleifend finibus lorem. Donec a volutpat libero, at vehicula tellus. Vivamus tellus orci, pharetra in nisi vitae, tincidunt dapibus nunc. + +Suspendisse ultricies sem laoreet quam molestie ullamcorper. Sed auctor sodales metus, eget convallis eros ultrices quis. Duis rutrum mi a tortor iaculis posuere. Suspendisse potenti. Pellentesque dictum faucibus dui a vestibulum. Donec elit nisl, aliquet at dolor non, viverra dignissim felis. Donec mi elit, condimentum sit amet magna id, efficitur sodales arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque est elit, porttitor eget bibendum nec, auctor nec nulla. Pellentesque dignissim nisl vel orci commodo, ut ullamcorper justo viverra. + +Suspendisse pharetra gravida turpis sit amet efficitur. Nulla mattis tortor eget pharetra ultrices. Ut a turpis maximus, pharetra justo non, tempor quam. Nam et volutpat lectus. Vestibulum congue turpis augue, quis eleifend nulla euismod in. Vestibulum sed erat tempus, porttitor diam vel, elementum metus. Aenean facilisis molestie ante, sit amet ornare lacus mollis sed. Maecenas rutrum urna nec ex malesuada consequat. Nunc interdum pellentesque nisl sed viverra. Nam nec fermentum ipsum. Nulla facilisi. Maecenas congue dolor erat, a fermentum enim congue vitae. Integer metus libero, feugiat at eleifend eu, iaculis nec leo. Praesent eleifend convallis leo, eu posuere urna elementum eu. + +Aliquam dapibus dolor vel urna luctus venenatis. Suspendisse potenti. Donec vitae tellus nisi. Pellentesque vel neque dignissim, ornare tellus sed, sodales metus. Aliquam vitae maximus tortor. Nullam mattis, odio id porttitor euismod, est leo aliquet massa, bibendum pulvinar justo orci a magna. Morbi ut nisl congue, porttitor erat non, gravida turpis. In nulla risus, ullamcorper quis suscipit vel, tristique ut nulla. In malesuada odio augue, ac hendrerit nunc mattis a. Nam in quam ut elit ullamcorper mattis. Sed fringilla tempus felis, id pretium tortor varius non. Aenean quis sem quis risus mattis posuere vel quis nibh. + +Sed pulvinar commodo dui sit amet malesuada. Quisque porta tellus placerat congue efficitur. Cras id blandit enim. Praesent a urna felis. Aliquam ornare, nisl eget iaculis tempor, ligula mi vehicula dolor, ut eleifend massa massa vel est. Fusce venenatis laoreet lobortis. Proin tempus aliquet nunc ut porttitor. In est mauris, blandit eu mattis vitae, aliquam a orci. Cras vitae nulla nec ex cursus blandit. Aliquam fermentum, erat in aliquet dictum, metus urna fermentum quam, non varius magna lectus non urna. + +Donec faucibus nisl nunc. Integer rutrum dui enim, malesuada semper turpis pulvinar eget. Fusce consectetur ipsum tellus, sed maximus lorem auctor in. Morbi nec est in quam ultricies hendrerit. Sed aliquam sollicitudin elementum. Aenean aliquam ex sit amet dignissim venenatis. Duis malesuada leo nisi, id accumsan turpis pretium id. Sed ornare magna non pharetra pharetra. Ut auctor dolor neque, nec bibendum odio viverra in. Nulla convallis interdum condimentum. Proin vestibulum turpis in lorem ultrices, bibendum tristique ligula faucibus. In tincidunt auctor sem, vitae fringilla neque pulvinar eu. Etiam commodo pellentesque enim. Phasellus feugiat non sapien eget sollicitudin. Etiam consequat efficitur lacus vel maximus. Suspendisse vitae elementum diam. + +Mauris urna velit, efficitur at viverra at, interdum a velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean bibendum sagittis massa in interdum. Mauris facilisis dui eget ipsum euismod ultrices. Donec sit amet lorem iaculis, condimentum velit quis, lacinia justo. Integer faucibus metus sed eros semper, in congue tortor venenatis. Proin tincidunt maximus enim, accumsan facilisis tellus gravida eu. Phasellus interdum aliquam ante, sit amet rhoncus nisl ornare at. Suspendisse libero eros, cursus quis fermentum nec, tempor eget lectus. Aenean ullamcorper augue lacus, non iaculis dui rutrum id. Aliquam erat volutpat. + +Morbi hendrerit fermentum sodales. Proin rutrum congue auctor. Mauris pellentesque elit non velit condimentum tincidunt a sit amet velit. Etiam accumsan ante id neque commodo vulputate. Aenean nec mattis neque. Aliquam tempor urna quis nisl convallis congue. Praesent vitae porttitor ante. Mauris mattis vestibulum ante, nec auctor augue. Praesent sem leo, accumsan eu fermentum egestas, iaculis ac nulla. + +Etiam vel nisi congue, varius neque id, volutpat quam. Fusce placerat arcu hendrerit orci condimentum vehicula. Integer sem ex, facilisis et lacinia a, condimentum at ante. Morbi condimentum diam tellus, non lobortis arcu pellentesque sit amet. Phasellus imperdiet augue eget sollicitudin mollis. Vivamus sollicitudin arcu aliquam lectus tristique, in consequat diam egestas. Suspendisse aliquet non velit id feugiat. Sed eu est fringilla, gravida nulla ac, dignissim tortor. Phasellus pellentesque nisl non venenatis sagittis. Etiam facilisis nulla quis lorem scelerisque vehicula id at ex. Duis in risus enim. In eu vulputate massa, vel dignissim odio. Praesent ut mi tellus. In hac habitasse platea dictumst. + +Nunc malesuada turpis in fermentum lobortis. Donec blandit eu orci non laoreet. Suspendisse posuere blandit tortor, in accumsan velit cursus in. Integer suscipit justo nulla, in viverra orci suscipit et. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum a pulvinar lacus. Vestibulum mattis nisi vel nunc convallis maximus. Fusce aliquam, erat in volutpat gravida, elit odio feugiat ante, nec varius enim leo eget nisl. Donec sit amet ligula lobortis, sollicitudin dolor quis, blandit augue. Duis at interdum sem. Nullam eleifend ligula urna, vitae sollicitudin purus cursus nec. + +In commodo risus eu justo hendrerit, ut posuere mi eleifend. Suspendisse sollicitudin odio sem. Vestibulum at dapibus dui, vel dictum nisi. In hac habitasse platea dictumst. Sed ac pharetra ex. Ut ultrices augue ut vulputate condimentum. Phasellus convallis arcu tortor, ac tincidunt justo cursus vitae. Mauris dignissim dapibus imperdiet. Nullam id quam eget mauris cursus molestie finibus eu enim. Fusce laoreet orci eu nunc fermentum tincidunt. Pellentesque vitae ex ac nunc porta mattis sed ut ligula. Phasellus erat dui, consequat et lacinia vel, blandit nec dui. Donec ipsum magna, rhoncus ac viverra vitae, feugiat vel ligula. + +Cras ut nisl sed elit dictum semper a at magna. Aliquam laoreet viverra velit vel lobortis. Nam tempor lorem sit amet purus tincidunt accumsan. Vestibulum et vestibulum ligula. Donec sit amet neque faucibus leo rutrum semper. Maecenas scelerisque, lacus et lobortis congue, purus quam euismod risus, et mattis orci nisl eget est. In hac habitasse platea dictumst. Pellentesque eros velit, sollicitudin quis ante vel, blandit maximus mi. Suspendisse at eros id quam vulputate ornare. Duis placerat tellus vel odio ultrices, ac feugiat enim placerat. Vestibulum ut massa mattis, commodo orci euismod, cursus lacus. Suspendisse potenti. Sed venenatis cursus neque, at lacinia erat ornare et. Sed rutrum, dui at porttitor hendrerit, lacus magna fringilla quam, id mollis elit leo ut ante. Praesent vel diam sed urna mollis laoreet eget ut risus. + +Mauris varius odio lectus, sit amet consequat nulla sollicitudin sed. Suspendisse commodo rhoncus enim vitae pellentesque. Aliquam vulputate sollicitudin aliquet. Mauris interdum interdum orci, non varius sem ornare ut. Sed vel nibh nunc. Duis tincidunt quam quis lectus egestas, eu ultricies ante posuere. Aenean in mauris eros. Suspendisse potenti. Vivamus sit amet augue at velit accumsan fermentum. Phasellus vel dui sit amet felis convallis sodales. + +Nunc mattis vitae sapien ut dignissim. Nam fermentum sit amet massa eu accumsan. In ut ipsum sit amet ante pellentesque accumsan. Nulla egestas eros eget lacus rhoncus pharetra. Nam pellentesque laoreet ex in lobortis. Vivamus congue tincidunt molestie. Integer vel turpis augue. Cras vel molestie quam. Etiam vel mauris ut tellus finibus consequat. + +Curabitur velit erat, vestibulum blandit massa a, aliquam elementum tellus. Pellentesque id nisl tempor lorem condimentum dapibus. Quisque ut lorem at orci elementum dapibus quis ut enim. Praesent venenatis congue arcu eu sagittis. Nunc nunc massa, posuere ac gravida id, scelerisque nec velit. Suspendisse non lacus a orci dapibus congue. Sed leo velit, facilisis sed egestas ut, vehicula at turpis. Praesent a finibus felis. Quisque est mi, pellentesque sit amet varius eu, gravida id est. + +Donec auctor gravida urna ac suscipit. Etiam placerat ipsum vitae ante congue, at fringilla lectus ullamcorper. Donec viverra lacus ut erat posuere, dignissim porta purus tempor. Duis eget enim quis diam vehicula pulvinar. Donec tempus eros velit, sit amet pharetra ligula placerat in. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet pretium tellus, non dictum ligula. Nullam a ex purus. Vestibulum id ex rhoncus, pretium eros vel, consequat tellus. Vivamus lacinia dolor nec ipsum aliquam, euismod fermentum sem efficitur. Nulla facilisi. Pellentesque pharetra lacinia augue, et eleifend lorem aliquet eu. Ut ut porta ante. Duis ut auctor nisi, id porttitor erat. Proin sollicitudin sem quis justo sodales aliquam. Nulla pharetra gravida arcu vel tempus. + +Maecenas nec imperdiet nulla, vitae fringilla diam. Mauris maximus aliquet tellus at congue. Aliquam in dictum elit. Sed pretium mattis lectus, non pellentesque enim dignissim vel. Sed quis elementum diam, a porttitor enim. Cras cursus eros nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ornare sapien vel diam vestibulum, at commodo metus lobortis. Sed euismod iaculis scelerisque. Pellentesque venenatis malesuada dolor, at tempus eros venenatis sit amet. Fusce a massa non libero blandit suscipit. + +Nulla facilisi. Sed nec leo nisl. Proin condimentum nunc at risus semper, in laoreet dui congue. Integer in dolor vitae ex maximus porttitor. In efficitur vulputate metus, ac egestas diam pharetra ac. Sed tristique tempus ligula dignissim convallis. Ut tincidunt laoreet fringilla. Praesent nunc est, lacinia tristique odio in, varius rhoncus ipsum. Vivamus bibendum justo at metus ullamcorper imperdiet. Quisque elit nibh, rhoncus a placerat eget, vehicula quis ante. + +Morbi fermentum turpis enim, eu interdum dui interdum sit amet. Sed vulputate lacus nec ligula gravida euismod. Duis in nisl fringilla, sollicitudin diam ac, laoreet tortor. Sed eget porttitor augue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed eu enim convallis augue vulputate convallis. Praesent laoreet, leo at ornare luctus, nisi felis semper sapien, sit amet sodales augue mauris eu lorem. Fusce ornare volutpat malesuada. Curabitur vehicula, eros id fringilla placerat, tellus elit venenatis lorem, ac imperdiet purus ex blandit felis. Nunc suscipit elementum risus ac dictum. Aliquam bibendum dignissim ipsum, sit amet posuere sem viverra ut. Vestibulum egestas in ex ac volutpat. + +Donec ut faucibus velit, nec suscipit metus. Nullam dui ligula, commodo eget est venenatis, ullamcorper porttitor lectus. Suspendisse dictum, metus vitae commodo ultricies, enim quam pretium enim, a efficitur tortor purus sed ipsum. Nam sit amet lacus vel elit consectetur ultrices. Vestibulum ac nibh in metus porttitor vestibulum. In blandit et est non efficitur. Aenean vestibulum tortor sit amet mattis semper. Praesent sit amet turpis ac neque malesuada tincidunt nec nec urna. Mauris posuere elit nisl, ac ullamcorper nisi pulvinar in. Nulla ac elit in neque ullamcorper facilisis sed et dui. + +Quisque molestie euismod dui, non scelerisque arcu dictum a. Donec eu nulla nisl. Aliquam neque orci, ultrices in nunc vitae, sollicitudin eleifend augue. Etiam at mattis velit, a efficitur dui. Nam bibendum enim non elit tristique aliquet. Vestibulum eget nibh lacus. Pellentesque id sapien dui. Nullam urna leo, faucibus et efficitur vel, ullamcorper iaculis mi. Donec sit amet bibendum justo, id dapibus sem. Pellentesque dignissim varius tellus nec egestas. Praesent eu orci vel ipsum pharetra maximus. Cras nisl ligula, sollicitudin in ligula vel, posuere sagittis eros. Phasellus porta tristique mauris vel eleifend. Mauris sit amet volutpat ipsum, sed vestibulum orci. + +Ut rutrum augue quis rhoncus tincidunt. Aenean sollicitudin lacus at erat varius ullamcorper. Integer vitae orci vel nisi vulputate cursus eget nec ex. Mauris elementum, augue ac accumsan convallis, libero leo porta ante, sit amet elementum felis ligula non enim. Ut venenatis semper posuere. Vestibulum nec nisl nisi. Ut a sapien ac orci finibus dictum sed at ex. Phasellus ac lorem nisl. Quisque vitae tempor lacus. Vestibulum in mauris diam. Proin mattis ligula vitae ipsum bibendum, at dapibus nunc placerat. Nam iaculis justo in accumsan tristique. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris pretium velit et tristique pellentesque. Nunc in sapien a purus congue rutrum. Nam placerat risus in ante rutrum rutrum. In in ligula magna. Duis orci ante, vehicula elementum consectetur vitae, lobortis vitae arcu. Ut feugiat tempus metus quis sollicitudin. Etiam cursus venenatis augue at fermentum. + +Vestibulum id vehicula massa. Proin ut ligula et sapien placerat tristique non nec nibh. Etiam non lacus placerat, molestie ex eu, venenatis elit. Sed posuere, ante et ullamcorper luctus, elit lectus blandit tortor, nec consequat massa elit a tortor. Fusce urna felis, porttitor at ultrices vel, eleifend porta ipsum. Pellentesque nisl nibh, molestie sit amet sem eu, dapibus pharetra nulla. Phasellus viverra augue eu augue volutpat dignissim. Integer bibendum faucibus varius. Curabitur non turpis purus. + +Sed pretium eros nisl, sed ullamcorper magna faucibus quis. Etiam ornare euismod tellus, vitae accumsan eros bibendum ut. Morbi a ex sed risus faucibus rutrum et ut urna. Nullam non tortor commodo, facilisis massa non, tristique metus. Curabitur placerat, arcu in egestas ullamcorper, nisl nisl luctus felis, ac dapibus erat velit sed erat. Proin sagittis felis vitae sem posuere semper. Vivamus fermentum eu nisi ac facilisis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque pellentesque lacinia ex tempus lacinia. Aliquam erat volutpat. Nam aliquet mattis risus non pretium. Suspendisse potenti. + +Suspendisse et sapien ornare, elementum erat vel, ultrices nisi. Curabitur interdum dignissim tincidunt. Cras eget est a velit consectetur finibus et sed nisl. Curabitur vestibulum semper posuere. Aliquam porta diam commodo nulla tempus imperdiet. Sed id dictum neque. Ut sit amet risus aliquet, dignissim velit sed, tristique ipsum. Nam a sapien id magna commodo tincidunt quis ac tellus. Nunc nec pellentesque orci. In justo purus, vulputate quis urna a, fringilla blandit sem. Cras porttitor quam vitae metus vehicula faucibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut eleifend orci lectus, vitae semper eros hendrerit id. Fusce nec tellus condimentum, vehicula elit quis, gravida erat. Maecenas volutpat interdum velit id vestibulum. + +Pellentesque id metus metus. Nullam ac metus non nisi facilisis aliquet quis a sem. Morbi cursus consectetur aliquam. Morbi id sapien nibh. Etiam tempus tempor tempor. Nam scelerisque condimentum purus. Proin nec cursus eros, in condimentum dolor. Nam in sagittis urna. Ut eget ornare erat. Vivamus nec elit ut sapien tristique aliquet a nec urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas varius pellentesque diam. + +Ut eget libero iaculis urna porta iaculis vel ac eros. Sed sed mi et libero porta consequat a in libero. Sed in imperdiet ipsum, sit amet ultricies dui. Curabitur sit amet consectetur eros. Praesent nunc tellus, feugiat ac laoreet consectetur, accumsan nec magna. Praesent at elementum orci. Donec dapibus venenatis libero, id tincidunt libero euismod ac. + +Aenean sit amet ex placerat, molestie sapien a, volutpat turpis. Vivamus elementum lacinia nisi a convallis. Donec a sagittis turpis. Curabitur pulvinar laoreet dolor id consequat. Aliquam aliquam feugiat magna, vitae sagittis lorem mattis ut. Donec sed auctor nulla. Ut convallis, neque vitae faucibus efficitur, nisi justo pharetra odio, vitae tristique purus lorem et nisi. Quisque eleifend aliquam arcu nec maximus. Nunc mauris elit, finibus nec gravida non, maximus nec nibh. Sed eu ipsum in arcu gravida maximus. Maecenas massa dolor, ullamcorper eget odio eu, congue ornare leo. Suspendisse venenatis ultricies imperdiet. Nam finibus orci vitae sagittis finibus. Pellentesque sit amet dapibus diam. Nam eu nunc elit. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam euismod turpis nec urna lobortis, ut malesuada ex suscipit. Fusce sed viverra risus. Pellentesque tristique nulla pulvinar tincidunt accumsan. Nullam vitae nibh imperdiet erat rutrum pellentesque et sit amet quam. Aenean laoreet ultricies hendrerit. Duis elementum tellus a feugiat vulputate. Aliquam aliquam enim placerat dolor viverra, non suscipit lorem ultrices. + +Proin interdum vitae lorem quis viverra. Praesent nec tempor dolor, vitae sagittis turpis. Etiam sit amet imperdiet sapien, ac laoreet arcu. Proin aliquam, risus nec maximus dapibus, dui diam elementum dolor, vitae tempor augue lorem vel justo. Aenean ac egestas dolor. Ut aliquet, felis at fringilla venenatis, leo nisl ullamcorper ex, vitae pellentesque turpis orci quis lectus. Nullam vitae suscipit metus. Integer congue, ante in lacinia rhoncus, erat lorem interdum elit, egestas suscipit lectus nunc non orci. Nunc vel viverra quam. Mauris sit amet sodales tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis sollicitudin libero. In nec venenatis orci. + +Integer non quam fringilla, tristique nulla id, gravida arcu. Aenean scelerisque lacinia magna. Praesent nunc sem, lobortis non convallis rhoncus, rutrum vitae ante. Sed et orci ut erat viverra bibendum a et lectus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce sit amet eros eget dolor pharetra vestibulum. Nullam vestibulum, massa dictum finibus egestas, orci nulla pulvinar sem, nec tempor libero lacus eu ante. Nam lacus erat, gravida eu placerat sit amet, facilisis sed urna. Suspendisse efficitur felis non ornare dictum. Nam sit amet nulla enim. Nulla eget scelerisque est. Suspendisse lacinia velit arcu, id lobortis felis facilisis in. + +Suspendisse potenti. Nulla porttitor metus ut nunc dictum tristique. Cras sit amet tortor eget ligula tristique efficitur. Ut at nisi id purus imperdiet laoreet. Sed sit amet malesuada urna. In nunc dolor, luctus ac condimentum ut, dapibus vel metus. Suspendisse pretium urna eget libero convallis vestibulum. Integer ut mauris hendrerit ex posuere euismod sed sed odio. Nulla egestas libero sit amet magna venenatis faucibus. Pellentesque semper vestibulum elit, et pretium felis scelerisque non. Suspendisse aliquet leo arcu, sed dapibus ex semper non. Donec lacinia dictum dignissim. Maecenas ipsum nunc, aliquet eget consectetur sit amet, aliquet vitae odio. + +Proin consectetur blandit feugiat. Nulla ac dictum quam. Vivamus suscipit scelerisque ipsum, vitae consequat neque sagittis id. Donec eu augue hendrerit, varius ipsum at, cursus massa. Morbi id augue id ipsum porttitor mollis. Phasellus nec libero eu arcu finibus dapibus. Nullam nec pharetra ante. Aenean sit amet urna eget justo tempus pharetra ut nec mi. Pellentesque viverra, ligula nec elementum ornare, ante elit eleifend enim, nec dignissim ligula elit in nibh. Vestibulum facilisis, felis eu condimentum dapibus, ante risus tincidunt urna, ut posuere dui augue ut ante. + +Nam arcu lacus, accumsan ac dolor in, egestas euismod est. Sed consectetur mauris et enim tincidunt semper. Donec sit amet pellentesque diam. Fusce viverra arcu a placerat fermentum. Nullam euismod dui eget egestas ultrices. Duis euismod viverra tortor eget eleifend. Pellentesque vitae neque dapibus, bibendum mi eu, auctor velit. + +Pellentesque congue consectetur turpis, eget auctor dui suscipit vel. Aliquam a sollicitudin turpis. Praesent nec blandit tortor. Suspendisse non nunc at urna tincidunt elementum. Integer eu elit nec urna maximus blandit quis at tortor. Nulla laoreet elit a purus tempus, et tincidunt lorem sagittis. Aenean semper erat eu neque hendrerit ornare. Cras posuere lorem nec orci vulputate finibus. Fusce tempor ex ac lacus gravida venenatis. + +Phasellus laoreet, libero vel fermentum euismod, sem diam accumsan quam, vitae gravida arcu est at sem. In bibendum, felis at viverra congue, felis nunc fringilla libero, ut scelerisque erat massa ut neque. Suspendisse potenti. Cras faucibus dolor vel tortor blandit, quis imperdiet magna semper. Nullam ut urna dapibus, vulputate est a, hendrerit purus. Vivamus vestibulum nisi a tempus placerat. Morbi vitae ultricies lacus. + +Nunc vel efficitur urna. Fusce bibendum suscipit mauris, quis imperdiet nisl bibendum non. Nam sed nisi imperdiet, pellentesque sem sed, pellentesque diam. Praesent luctus feugiat odio, eget fermentum lectus ullamcorper non. Phasellus pulvinar lectus sed ligula semper lobortis. Pellentesque fermentum ultricies fermentum. Quisque id turpis vel orci rutrum rhoncus id vel metus. Vivamus ex leo, accumsan eu luctus sit amet, tincidunt vitae erat. Sed eu mollis odio, ut ultricies lacus. Duis enim odio, pellentesque non velit vel, aliquam blandit erat. Mauris feugiat felis fermentum purus blandit, id rhoncus lorem tempus. Integer cursus, nunc vitae laoreet commodo, nunc lacus venenatis orci, quis eleifend risus est nec quam. Nulla tincidunt nunc ac purus tincidunt, eu molestie ipsum sollicitudin. + +Vestibulum ac varius velit, non suscipit nulla. Vestibulum a sagittis tortor. Suspendisse vestibulum felis quam, eget blandit nulla dictum vel. Praesent eu tellus ut lacus facilisis ultrices. Etiam fringilla nulla nec leo viverra faucibus. Sed nec sollicitudin nisl. Aenean libero massa, ornare sed elit ut, interdum fermentum arcu. + +Aliquam maximus diam et elit dapibus, eget condimentum sapien finibus. Etiam gravida nunc dapibus facilisis feugiat. Sed quis massa ligula. Nunc eleifend dolor lacus, at dapibus nisi vulputate eget. Etiam vel euismod urna, quis molestie nibh. Vestibulum bibendum erat non dictum sollicitudin. Suspendisse sed placerat lacus. Cras sollicitudin quam justo, a condimentum urna feugiat a. Quisque mauris libero, ultricies at ligula a, facilisis euismod ante. + +Morbi hendrerit maximus sapien ut auctor. Nullam nisi erat, aliquam ac metus eu, fermentum laoreet augue. Nam ultricies mi at vulputate laoreet. In ipsum est, blandit sit amet lorem id, egestas aliquam odio. Praesent venenatis lobortis mi. Aenean eu lacus consequat, mattis enim et, pellentesque leo. Duis convallis facilisis placerat. Donec porta, enim eget placerat congue, nisi augue faucibus odio, et fringilla purus elit vitae felis. + +Nulla et tellus a libero pellentesque eleifend vitae mattis nisi. Nunc placerat neque et sapien lobortis, aliquet pharetra nunc vulputate. Suspendisse potenti. Etiam ullamcorper lacinia varius. Suspendisse sit amet malesuada magna. Nam molestie mi nec diam tempus laoreet. Suspendisse urna tellus, scelerisque vitae purus et, dapibus fermentum felis. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec scelerisque eget neque sed hendrerit. Donec at ligula augue. Donec vulputate massa nunc, a feugiat nunc elementum ut. Donec pulvinar libero sit amet ante faucibus sagittis. Mauris quis diam ultrices, tristique sapien nec, tempus urna. Morbi hendrerit odio sit amet leo lacinia, non egestas diam condimentum. Suspendisse efficitur sapien eget elit euismod tristique. Duis posuere vestibulum risus id sodales. Ut eget mi in urna rutrum molestie non nec dolor. Curabitur sollicitudin pharetra lacus, in sodales tortor tempor vitae. Nullam rhoncus arcu vel euismod varius. + +Aenean malesuada, purus quis volutpat sollicitudin, lectus risus lacinia mi, a facilisis ante lacus a dolor. Aenean vel aliquam tortor. Vivamus ornare, tellus at malesuada suscipit, quam dolor egestas erat, id convallis enim augue a enim. Aenean ultricies sodales placerat. Vivamus vel erat ac mi vulputate commodo nec eget urna. Suspendisse potenti. Mauris quis dapibus est, id tristique libero. + +Suspendisse ex diam, imperdiet quis dui id, interdum sodales elit. Maecenas at nunc ligula. Etiam imperdiet convallis magna sit amet tristique. Proin hendrerit laoreet magna, eget malesuada elit rhoncus et. Curabitur eget odio imperdiet, molestie sapien pretium, efficitur turpis. Aliquam sed congue arcu. Pellentesque vitae mauris id neque pulvinar tempor. Donec lobortis tellus id gravida dictum. Nulla et varius ex. Vestibulum pharetra eu quam dapibus finibus. Nullam accumsan sagittis turpis. Mauris fermentum orci et tortor tempus, ac tristique odio sollicitudin. Duis nec elit et magna imperdiet molestie eget vel ante. + +Suspendisse tempus augue nec massa molestie aliquam. Pellentesque vestibulum, erat sit amet fringilla fermentum, sapien lorem tempor felis, at efficitur augue erat quis nisi. Proin nec felis sagittis, sollicitudin turpis eu, fringilla leo. Vestibulum at augue nec dui vestibulum aliquam a at metus. Duis ullamcorper eleifend bibendum. Maecenas viverra mauris lacus, et consequat nisl pharetra eu. Praesent rutrum diam eu mi aliquet, non pellentesque est suscipit. Vestibulum massa leo, blandit eget mi at, cursus faucibus nunc. Praesent nec bibendum libero, vitae cursus libero. Sed consequat vitae eros nec maximus. + +Pellentesque et tortor eget lectus feugiat venenatis. Integer ornare nisl quam, nec imperdiet justo finibus at. Morbi malesuada, ante sit amet rutrum tempor, risus lorem tristique urna, egestas varius purus ex ut sem. Etiam sit amet feugiat sapien. Etiam quis risus commodo, eleifend velit quis, tincidunt enim. Mauris fermentum lacinia velit quis efficitur. Nunc sit amet lacus sit amet urna viverra feugiat. Nullam sagittis rhoncus suscipit. Aliquam eu sem ligula. Sed sodales risus et tortor lacinia tristique. Nulla massa augue, malesuada sit amet euismod ac, euismod placerat nulla. Sed lobortis ex nec lacus facilisis, nec semper mauris ultrices. Quisque ornare non felis vitae pellentesque. Donec mattis ut magna sed imperdiet. Integer viverra tempus feugiat. + +Donec laoreet aliquam imperdiet. Fusce justo neque, dictum vitae fringilla vitae, euismod sed augue. Fusce sodales, lectus a congue bibendum, ante eros pharetra tortor, eu sollicitudin libero ipsum sit amet felis. Curabitur sed condimentum quam, in iaculis ante. Ut feugiat dictum odio, vitae hendrerit lacus volutpat sed. Sed scelerisque et metus nec gravida. Mauris mattis porttitor magna, ut maximus justo sagittis vitae. Donec at metus ut leo gravida vehicula in sed diam. Vestibulum dignissim suscipit sagittis. Nam varius et arcu sit amet lacinia. Sed eget elit rhoncus, elementum dolor a, vehicula orci. Nam vitae tellus sapien. Phasellus massa lorem, commodo quis placerat in, semper faucibus tortor. + +Vivamus id dolor mi. Curabitur sagittis posuere quam in mollis. Phasellus finibus ipsum vel elit tincidunt, a bibendum ligula vulputate. Suspendisse quis purus nisl. Integer mi sem, posuere quis nisi a, consequat consequat magna. Vestibulum bibendum mauris in risus auctor fringilla vel at lacus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus venenatis mauris at mattis eleifend. Donec tempus ipsum ac nisl convallis ultrices. + +Nullam eu nibh ut erat vehicula accumsan. Vivamus vitae faucibus libero. In in luctus odio. Nulla nec enim pharetra, fermentum erat in, tempor turpis. Sed ac magna suscipit, dignissim elit id, faucibus libero. Maecenas placerat in dolor et faucibus. Sed maximus purus dolor, non egestas massa rutrum non. Nunc ut rhoncus lacus. Sed sit amet dolor eu sapien sagittis molestie. Aenean ipsum erat, rutrum a diam et, varius porttitor ligula. Phasellus fermentum fermentum felis. + +Phasellus odio massa, lacinia ac hendrerit vestibulum, placerat sit amet erat. Praesent eget justo scelerisque, congue est in, pharetra mi. Etiam blandit turpis dui, a faucibus ipsum viverra vitae. Praesent sed scelerisque arcu. Cras nec venenatis ipsum. In et tempus metus. Pellentesque cursus quis nibh quis porttitor. Duis leo lacus, pretium eget rutrum eu, tincidunt lobortis tellus. Cras in erat tristique arcu faucibus luctus sit amet tempus erat. Nullam placerat, est a interdum iaculis, purus justo convallis arcu, at mattis erat sem tempor velit. Curabitur sapien sapien, tempor eget sodales vitae, blandit ac libero. Proin eget sem et arcu malesuada convallis non a est. + +Ut id sagittis urna. Morbi est mauris, molestie quis magna ut, convallis porta justo. Etiam in vulputate sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam consectetur enim nisl, sit amet pulvinar turpis elementum at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum convallis cursus libero vel feugiat. + +Curabitur vel scelerisque metus. Etiam aliquet faucibus quam, vel aliquet est mattis quis. Pellentesque pulvinar sodales augue, a dignissim sem tristique vitae. Pellentesque dolor quam, pharetra vitae rhoncus ut, blandit at elit. Sed dignissim diam turpis, ac condimentum justo iaculis vel. Donec facilisis eros sit amet est dapibus, vel pellentesque eros consequat. Integer euismod mollis metus, vel rutrum risus imperdiet ac. Fusce semper dolor sit amet mi congue accumsan. Maecenas sagittis magna justo, vel varius ante scelerisque id. Fusce at urna sapien. Aliquam dui sapien, facilisis eu magna laoreet, feugiat tincidunt elit. Fusce iaculis sit amet erat id dignissim. Suspendisse sollicitudin laoreet consequat. Pellentesque eu mollis quam, vel dapibus libero. Duis mattis tortor vitae orci vehicula, et viverra ipsum lobortis. Quisque iaculis sapien est, id sagittis tortor rutrum at. + +Duis ut condimentum risus, et venenatis nulla. Praesent urna ex, faucibus eu mollis nec, lobortis vitae neque. Vivamus a malesuada tellus. Quisque bibendum dolor nec massa porta, nec malesuada magna auctor. Phasellus non auctor turpis, et bibendum risus. Ut pellentesque justo non neque convallis, ut imperdiet diam sagittis. Nunc arcu nisi, rutrum sagittis neque elementum, finibus consequat felis. Proin euismod elit eu urna tristique ultrices. + +Curabitur id hendrerit ipsum. Aliquam tortor nisi, dignissim vel sodales a, ultricies a ipsum. Etiam commodo arcu sed volutpat varius. Proin vehicula lacinia tempor. Vestibulum feugiat nibh eu ante tempor efficitur. Etiam eleifend a orci eu euismod. Cras a tortor risus. Cras nec urna non urna malesuada maximus vel et ipsum. In ullamcorper porttitor dolor, at fringilla tortor tristique ac. Nulla gravida tortor nec dolor convallis, accumsan sollicitudin dui luctus. Vestibulum euismod vestibulum semper. Nam semper lacus dolor, vitae rhoncus nisi blandit nec. Phasellus turpis dolor, posuere sed sodales sit amet, interdum ut arcu. + +Sed blandit nulla et sem vulputate, et semper lacus posuere. Proin velit lorem, sagittis tincidunt aliquet vitae, fermentum sed orci. Ut interdum ultrices dolor eu tempor. Quisque pretium vulputate dui at blandit. Aenean pretium urna sed purus dapibus aliquam. Mauris tristique augue pretium magna scelerisque, vel cursus sapien porttitor. Nullam neque justo, aliquam non neque id, lobortis facilisis nibh. Duis molestie dui eu augue tristique porttitor. Sed posuere justo massa, efficitur vulputate erat posuere non. Cras quis condimentum tellus. + +Etiam eleifend ipsum ut justo viverra porttitor. Nam bibendum enim nec ligula vestibulum, vel fringilla velit posuere. Praesent leo enim, varius sit amet nulla ut, sodales sollicitudin nunc. Phasellus hendrerit varius ex quis tempus. Suspendisse maximus laoreet enim, ut bibendum tortor. Ut non magna vehicula augue condimentum scelerisque. Aenean efficitur elit vel rhoncus euismod. Vestibulum sit amet sollicitudin dui. Quisque ac diam nibh. Nullam quam enim, volutpat eu turpis et, posuere consectetur neque. + +Ut tincidunt semper dictum. Etiam varius enim sit amet metus consequat malesuada in at ligula. Cras nisl dui, tincidunt a urna et, porttitor rhoncus velit. Mauris interdum imperdiet lectus ac mollis. Aliquam sollicitudin ornare mi, a varius nulla faucibus aliquet. Nunc fermentum tempor ullamcorper. Pellentesque laoreet libero condimentum pellentesque pharetra. Nullam sed eros vel erat vestibulum dictum. Nulla nec interdum dui, quis finibus nunc. + +Sed ac turpis in mi ultricies finibus sit amet et diam. Pellentesque sagittis non ligula et convallis. Suspendisse interdum est aliquet erat rhoncus semper. Donec pretium turpis ante, vel posuere mauris facilisis vel. Vivamus nibh ligula, pharetra sit amet hendrerit nec, vulputate ut sapien. Nulla ullamcorper condimentum metus vitae imperdiet. Ut eget ex purus. Nulla dapibus malesuada pharetra. Praesent sit amet ornare turpis. Nulla pulvinar felis eget arcu mollis vulputate. Vestibulum eget semper felis. Donec viverra justo id mi tempor, a mollis ipsum porttitor. Maecenas aliquet volutpat ante eget tempor. Duis ipsum nisi, scelerisque quis metus vitae, blandit faucibus nisl. Mauris rutrum nisi sed lorem dictum ultricies. + +Nulla dictum arcu augue, aliquet ornare ligula suscipit ut. Integer libero erat, bibendum quis arcu at, consequat luctus sem. Ut ut erat tincidunt, porta erat efficitur, bibendum neque. Maecenas eget convallis sapien. Nullam facilisis ex et lorem fringilla, ut convallis leo dictum. Duis porttitor, ex eu venenatis faucibus, erat augue dapibus leo, sit amet scelerisque neque dui sed arcu. Praesent at diam nec sem sodales condimentum. Vivamus vehicula urna tellus, a semper ipsum cursus id. Duis auctor enim erat, laoreet volutpat dui rhoncus maximus. Suspendisse pellentesque euismod lacus, at auctor purus. Suspendisse volutpat imperdiet diam, id laoreet est egestas at. + +Fusce lobortis ex vel condimentum convallis. Vivamus fermentum convallis dolor quis rutrum. Donec tempus ornare maximus. Mauris quam neque, dignissim rutrum maximus sed, volutpat sed lectus. Donec id augue gravida, pretium purus eu, sagittis tellus. Maecenas tincidunt gravida felis nec pulvinar. Fusce turpis urna, sodales non rhoncus nec, sodales ac ipsum. Suspendisse risus felis, imperdiet id malesuada fermentum, ultricies et erat. Suspendisse potenti. Pellentesque tellus ipsum, dapibus eget pellentesque eleifend, venenatis sed risus. Ut sed mollis nisl. Aliquam lobortis aliquam ipsum, et ornare magna bibendum ut. Proin quis justo vitae neque tincidunt maximus ultricies et purus. Nullam non semper turpis. Quisque non mauris odio. + +Donec commodo tempus egestas. Vestibulum at diam vel mi tincidunt finibus. Donec aliquam magna id eros tristique finibus. Cras ut enim sapien. Proin gravida risus a nisi dapibus, ac vulputate nunc laoreet. Donec at leo vel nulla iaculis feugiat sed et ante. Nulla a arcu at ligula sollicitudin semper sed eget est. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Ut dolor magna, luctus id elit ut, interdum viverra lacus. Aenean sed tempor metus. Aenean arcu dui, eleifend quis est sed, luctus lacinia nulla. Morbi id luctus libero. Maecenas sodales leo eu sapien maximus porta. Praesent gravida augue eu ligula pretium cursus. + +Duis aliquam luctus tortor, eu facilisis quam sodales sed. Phasellus feugiat venenatis lorem, in scelerisque est efficitur quis. Cras quis nisl nisl. Quisque magna felis, tempus nec pharetra et, tempor id ligula. Pellentesque sed dignissim velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris convallis iaculis posuere. Nullam orci velit, venenatis eget enim quis, molestie consequat dolor. Nulla egestas risus vestibulum sagittis blandit. Nullam dui lacus, tempus euismod sapien a, eleifend sodales justo. In id tortor neque. Vivamus faucibus ante ac odio vestibulum, vitae condimentum nibh consectetur. In rutrum hendrerit est, sit amet mollis ex aliquet tempor. + +Donec non tincidunt sem. Nullam dignissim felis nibh, et accumsan felis tristique sed. Maecenas id massa erat. Ut justo ligula, aliquet eu condimentum a, aliquet eget ex. Phasellus et neque eu nisl consectetur ullamcorper. Sed gravida efficitur nisi, vitae posuere nisi tincidunt sed. Duis vitae molestie metus, vitae finibus urna. Integer at lacus vitae turpis convallis feugiat a dignissim libero. In dolor nibh, aliquam eu malesuada in, tincidunt vitae nisi. Nullam at lectus risus. Integer vitae ligula interdum, congue nibh id, tincidunt dolor. + +Mauris risus quam, mollis eu consequat vitae, molestie sit amet risus. Vestibulum congue vulputate nibh sit amet ullamcorper. Nullam elit justo, hendrerit euismod elit nec, gravida tincidunt ipsum. Aenean tellus tortor, commodo vitae cursus vitae, finibus quis mi. Nam in tellus scelerisque, cursus magna a, elementum tellus. Praesent ut lacinia justo. Donec consectetur, mi nec faucibus viverra, nisl justo suscipit est, in consequat ex urna vitae orci. Fusce molestie feugiat ex sed dictum. Phasellus interdum eros dapibus sodales volutpat. Morbi elementum sapien ante, quis sagittis justo fermentum at. Maecenas vestibulum massa quis eleifend rhoncus. Praesent auctor ex at urna pellentesque facilisis. + +Duis tincidunt porta quam, in commodo orci dapibus interdum. Donec mattis erat ante, nec faucibus magna pharetra id. Aliquam dignissim ultricies auctor. Duis quis ex mi. Phasellus ipsum mi, volutpat quis augue nec, dapibus aliquet lectus. Aenean id eros mi. Fusce rhoncus iaculis magna, commodo commodo nibh pellentesque ut. Donec consectetur lacinia erat ut luctus. Mauris efficitur erat vitae sapien fringilla sollicitudin. Maecenas a egestas ipsum. Aenean placerat congue augue, ut condimentum lorem accumsan in. Morbi ac quam nunc. + +Nunc posuere faucibus semper. Integer at convallis ex. Duis a purus molestie, dignissim mi quis, consequat nulla. Proin tempus congue ligula, sit amet ultricies enim consequat ut. Nunc ac est at nisl euismod cursus. Pellentesque vulputate molestie ipsum. Praesent varius, lacus non lobortis blandit, nibh lacus scelerisque nisi, sit amet interdum ligula tellus ac purus. Vestibulum sed quam tempor, pharetra tellus ullamcorper, tincidunt est. Nulla tempus tortor nec erat tempus eleifend pellentesque eget purus. Duis gravida dui justo, ac commodo ipsum mollis et. Nullam vitae nibh enim. Ut sodales mi eu diam sagittis, quis luctus tortor euismod. + +Maecenas a augue ac augue varius rhoncus. Fusce nunc turpis, mattis eu iaculis a, dapibus nec purus. Pellentesque imperdiet sit amet purus a blandit. Morbi augue tellus, venenatis nec tortor in, consectetur tincidunt nulla. Aenean purus libero, volutpat quis neque vitae, mattis efficitur eros. Donec sodales venenatis augue, sed faucibus magna accumsan convallis. Pellentesque efficitur elementum imperdiet. Cras at condimentum leo. Curabitur feugiat ut nisi facilisis commodo. + +Sed commodo sapien quam, quis vulputate orci lobortis nec. Aenean luctus dictum ipsum, non consequat sapien molestie vel. Proin blandit libero tortor, vitae efficitur tortor hendrerit at. Sed eleifend eu velit eu tempor. Nunc auctor tincidunt mollis. Ut molestie, erat ut lobortis convallis, odio felis sollicitudin elit, vitae ultricies lorem neque et dui. Sed venenatis tempus ullamcorper. Integer eget elementum nulla. Maecenas a placerat ipsum. Etiam sagittis sagittis rhoncus. Aliquam faucibus magna id lacus pharetra, nec interdum lacus sodales. + +Mauris ipsum sem, venenatis non elit in, feugiat pretium lacus. Fusce eget tincidunt dolor. Nam pharetra lacus vitae diam bibendum, ac placerat tortor aliquet. Sed eget gravida orci. Ut in orci lorem. Morbi condimentum ligula dolor, in dictum mi vestibulum sed. Suspendisse eu velit finibus, tincidunt dolor malesuada, mattis est. Morbi iaculis sapien vitae metus facilisis fringilla. Donec sed volutpat neque. Mauris ipsum metus, venenatis cursus mattis quis, convallis ultricies purus. Aliquam quam magna, sagittis at mi quis, lacinia iaculis turpis. Praesent viverra, ex nec euismod lacinia, urna risus pretium odio, sit amet mattis metus eros non lectus. + +Nam sed vehicula est, ac aliquet mi. Aenean iaculis placerat orci, ut lobortis dui vestibulum convallis. Vestibulum eleifend ante sed lorem interdum lobortis ut vel tortor. Sed auctor id nisl sed bibendum. Fusce maximus vulputate mauris, tempus sollicitudin nunc efficitur vitae. Nulla pellentesque molestie leo, quis hendrerit enim. In vel dapibus nisi. Nam at dui quis eros porttitor volutpat in id magna. Maecenas quis quam a justo cursus aliquet viverra sit amet mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut hendrerit est at augue pretium condimentum at ut velit. Suspendisse non gravida metus. + +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Praesent lacinia commodo elit, sed fringilla ipsum imperdiet ut. Proin a dictum ante. Suspendisse potenti. Praesent ac nulla volutpat, ultricies diam vel, auctor risus. Nullam aliquam elit vel quam suscipit molestie vel ac dolor. Ut pharetra finibus elit, id vulputate ex dignissim in. Ut ac finibus urna, a luctus magna. + +Fusce suscipit convallis eros nec blandit. Cras quis lectus nibh. Donec pulvinar rhoncus pulvinar. Vivamus nulla nisi, vestibulum vel neque et, dictum gravida ex. Vestibulum in arcu vitae nibh auctor rhoncus ac in massa. Sed semper enim ac dui sollicitudin porttitor. Etiam ante erat, laoreet sed dolor eget, cursus commodo lorem. Curabitur maximus tincidunt nulla at molestie. Phasellus ultrices felis a cursus facilisis. Nullam a semper tortor. Nulla ac vulputate justo. Mauris maximus nisi quam, elementum eleifend nulla condimentum nec. Aenean congue tincidunt mi, id mollis orci blandit vitae. Integer placerat orci at nisl vestibulum lacinia. + +Sed egestas posuere egestas. Quisque pulvinar velit commodo felis accumsan, a consequat purus laoreet. Ut at arcu at augue sodales rhoncus. Ut non nisl dui. Sed sed nulla quis orci eleifend auctor ut et sem. Aliquam erat volutpat. Donec egestas tincidunt leo in interdum. Donec varius odio nulla, id porttitor erat venenatis ut. Nulla ac nisl ut enim placerat congue. Fusce consequat purus eget risus luctus finibus. Donec in blandit odio. Sed vestibulum nisl ut diam vestibulum volutpat. Donec magna quam, tempor eget velit quis, blandit tristique lacus. Pellentesque et orci dui. + +Integer tempor mollis purus ut volutpat. Pellentesque efficitur cursus neque in ullamcorper. Integer ac tincidunt lacus, at venenatis tellus. Curabitur convallis commodo enim a convallis. Aenean sagittis sodales nibh, sed eleifend diam ultricies at. Vestibulum erat mauris, lobortis ac tempor a, viverra non sem. Nulla non ipsum mollis, dignissim elit a, laoreet enim. + +In laoreet purus eget orci rhoncus viverra. Quisque vel metus quis nulla hendrerit viverra. In consectetur velit vitae purus vestibulum, in hendrerit odio sollicitudin. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ut est sed ligula tincidunt fermentum nec ac nulla. Nam in sagittis velit. Vivamus vel dapibus erat, ullamcorper sollicitudin metus. + +Quisque pulvinar nulla eget mi mattis, ut convallis nulla ullamcorper. Vivamus placerat mauris vel elementum aliquet. Sed ac accumsan eros. Vivamus vitae rhoncus urna. Fusce gravida cursus varius. In posuere finibus leo cursus molestie. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris vulputate mi ut nunc finibus convallis. Praesent luctus erat eu urna facilisis convallis. + +Nam efficitur tempus augue varius porttitor. Pellentesque scelerisque dolor scelerisque, dignissim massa eget, iaculis nibh. Praesent viverra molestie dui a pulvinar. Mauris malesuada mattis felis, vitae sagittis metus pellentesque viverra. Phasellus faucibus congue blandit. Pellentesque mattis, justo sed imperdiet rutrum, metus metus porta nisi, vel malesuada lorem massa at dolor. Nullam nisi eros, tristique quis mollis ac, hendrerit nec leo. Praesent viverra molestie vestibulum. Nullam eu aliquam risus, at dignissim diam. Quisque blandit fermentum erat, sed ullamcorper augue pellentesque in. Integer eleifend libero non lacus sagittis auctor. Aliquam volutpat interdum accumsan. + +Etiam in eros porta, bibendum lacus eget, feugiat orci. Integer massa dui, commodo ac luctus nec, ornare id velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis dignissim scelerisque ex. Aliquam tempus cursus nibh, sed scelerisque libero sagittis ut. Morbi finibus vestibulum nulla, nec aliquam urna venenatis vulputate. Pellentesque ultricies, lorem a dignissim bibendum, augue nisi eleifend libero, cursus ultrices nulla purus a nunc. Quisque dictum pulvinar turpis vitae finibus. Etiam eget ultrices diam. Sed commodo enim ante, fermentum rhoncus tortor tincidunt ac. Suspendisse fermentum aliquet erat non posuere. Aenean vel dapibus lorem. + +Aenean lorem libero, rutrum vel egestas vel, pellentesque ut ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum felis diam, pellentesque sit amet ultricies sed, vestibulum in leo. Morbi aliquet, quam ut fringilla sollicitudin, justo risus suscipit mi, in vulputate neque erat ut turpis. Duis auctor dui non urna sagittis dictum. Vestibulum nec felis vel sapien congue volutpat a a orci. Phasellus tincidunt libero ac lorem feugiat, ac elementum lectus eleifend. Praesent sit amet est id massa convallis congue. Integer ac nunc eget orci pharetra vehicula. Mauris et ultricies diam. Nunc et pellentesque lacus, eget bibendum sapien. Morbi condimentum mi ac metus euismod convallis. Proin consequat rhoncus varius. Maecenas feugiat elementum vulputate. + +Vestibulum tempor feugiat dapibus. Ut ornare in augue non euismod. Nulla faucibus gravida condimentum. Curabitur pharetra dui non lorem sagittis iaculis. Quisque vitae nibh magna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce quis pharetra sem, sed aliquet lectus. + +Fusce volutpat tempor tincidunt. Pellentesque ultricies imperdiet tincidunt. Integer porta dictum lorem, non luctus tellus bibendum sit amet. Quisque posuere felis et est dapibus, commodo rutrum leo molestie. Fusce sodales felis sed sem pharetra pretium. Praesent nec sollicitudin nisl. Donec volutpat convallis elementum. Duis id libero augue. Nulla facilisi. + +Nulla viverra, urna nec efficitur vulputate, metus odio lacinia purus, in sagittis elit erat vel massa. Fusce ligula leo, finibus non felis dignissim, dictum ullamcorper odio. Phasellus vel eros eu sem venenatis sagittis a nec nulla. Pellentesque sapien elit, lobortis quis dictum et, maximus vitae metus. Curabitur quis aliquam eros. Quisque cursus condimentum urna vel efficitur. Etiam aliquet lorem ut varius suscipit. In viverra molestie felis vitae vehicula. Curabitur cursus, nunc sodales faucibus ullamcorper, urna magna dapibus velit, accumsan blandit mi quam nec justo. Donec ac dui lorem. Sed eu sagittis nulla. Suspendisse ut ante lacus. Fusce non tristique felis. Nulla convallis consectetur arcu, semper egestas urna efficitur quis. Nulla ac turpis at leo pretium maximus. Morbi cursus diam ac purus imperdiet, non commodo tellus cursus. + +Mauris ut eros suscipit, suscipit nisi vel, porttitor sapien. Morbi in nulla sapien. Cras maximus pretium justo, vel eleifend urna vulputate sed. Aenean egestas nisl ex. Curabitur sed pharetra ligula. Nulla blandit lorem id mauris tincidunt, at elementum risus aliquet. Quisque id interdum dui. + +Aenean nec elit condimentum ex viverra hendrerit. Pellentesque blandit nibh elit, a ultrices orci vestibulum vitae. Donec ultricies malesuada justo ac luctus. Pellentesque laoreet nunc a sem varius, ac dapibus ligula volutpat. Aenean sem felis, aliquam nec ullamcorper quis, ullamcorper non ante. Pellentesque libero leo, sagittis nec tempus a, tincidunt eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec elementum massa vel diam suscipit suscipit. Suspendisse eget neque porta, cursus orci eget, imperdiet libero. + +Vivamus faucibus vulputate sapien, non pulvinar ex sodales vitae. Maecenas consequat est urna, id faucibus eros dictum at. Vestibulum tortor mi, porta vitae sagittis sed, convallis quis enim. Vestibulum gravida justo pulvinar sem ornare, efficitur dictum neque varius. Nullam egestas congue tellus vitae interdum. Nam lectus erat, porta vitae lorem non, mollis semper velit. Nulla vestibulum tincidunt elit. Phasellus sed vulputate leo. + +Sed efficitur quam ac iaculis volutpat. Praesent nec feugiat ex. Aenean at ligula iaculis, imperdiet lacus et, condimentum magna. Integer euismod leo id mauris condimentum, quis semper lacus blandit. Sed volutpat neque urna, sit amet ullamcorper est dignissim non. Vestibulum tristique sollicitudin risus, sed hendrerit massa finibus id. Vestibulum sit amet risus non eros vehicula malesuada. Nam facilisis lacus sed quam pulvinar, at fermentum lectus tincidunt. + +Vivamus laoreet sem nec odio blandit, ut ornare sem egestas. Suspendisse potenti. Nulla aliquam pretium volutpat. Maecenas a orci suscipit, aliquam eros a, maximus urna. Aliquam eu gravida justo, et hendrerit justo. Suspendisse a commodo lorem, quis viverra nisl. Nulla vel pellentesque nisl. Nulla ligula tellus, vehicula sit amet turpis in, sodales tincidunt tellus. Proin ut nibh sed ipsum porta dignissim vel sed mauris. Interdum et malesuada fames ac ante ipsum primis in faucibus. In hac habitasse platea dictumst. Vestibulum efficitur nulla rutrum, accumsan lacus at, dignissim tortor. Morbi egestas metus ut nibh tincidunt posuere at sed elit. Integer eget hendrerit nulla. + +Donec sagittis sagittis tempus. Proin iaculis neque vehicula commodo faucibus. Pellentesque erat ante, vehicula sed efficitur vitae, varius non sem. Mauris pellentesque, felis nec egestas scelerisque, nulla nunc fringilla arcu, feugiat fringilla quam neque in elit. Nullam ut ex odio. Mauris enim risus, consequat at suscipit at, pulvinar ut arcu. Fusce mollis sem sed tellus tempus pellentesque. In pharetra, libero sed tristique vestibulum, massa velit egestas risus, at mollis augue lorem sit amet turpis. Mauris risus dui, sagittis vitae congue ut, condimentum ut augue. Quisque non sollicitudin purus, sit amet consectetur sapien. + +Sed scelerisque ipsum sed augue varius, sit amet ultricies purus posuere. Etiam vehicula at nunc in posuere. Cras eget risus a sapien mollis facilisis. Morbi vel purus auctor, faucibus mauris non, malesuada leo. Donec venenatis consectetur libero in pretium. Ut efficitur molestie metus id scelerisque. Nunc dolor justo, pharetra vel sagittis vitae, placerat ut justo. Donec tempor fermentum est semper auctor. Nullam tincidunt risus non risus elementum varius. Suspendisse euismod augue metus, nec pellentesque enim sagittis ac. Morbi et lectus quis justo commodo varius commodo vel risus. In bibendum purus in erat ullamcorper, sit amet dignissim sapien scelerisque. Quisque tempus elementum rutrum. + +In viverra sollicitudin pretium. Sed finibus scelerisque sollicitudin. Nulla lobortis purus in lectus iaculis, a ornare ex accumsan. Morbi malesuada mollis placerat. In hac habitasse platea dictumst. Pellentesque sed dui quis odio scelerisque molestie eu nec libero. Proin viverra magna ligula, at luctus lacus posuere sed. Nullam non congue mi. Praesent non mattis neque, sit amet tempus diam. Mauris eget cursus lacus, id dictum mi. + +Sed semper velit in vehicula tristique. In lobortis est augue, et maximus tellus iaculis ut. Proin quis ex maximus erat iaculis imperdiet. Curabitur ultrices turpis ac nibh congue ornare. Fusce a aliquet felis, nec sodales tellus. Nunc mauris ante, feugiat et facilisis at, pharetra ultricies dui. Integer felis metus, porttitor ac ipsum vitae, placerat varius ex. Morbi in dui a nunc aliquam posuere. Nulla tempus tortor ac lorem consectetur sodales. Praesent sit amet placerat diam. Curabitur sodales mauris ante, et tincidunt lacus ultricies quis. Nulla eu finibus nisl, sit amet volutpat leo. Donec ligula enim, feugiat non aliquet nec, congue interdum lorem. Pellentesque a nisi blandit, semper massa sit amet, auctor eros. + +Aenean ligula libero, commodo a erat at, tristique facilisis quam. Sed congue fringilla lacus, vel iaculis quam suscipit ornare. Curabitur non pretium mi. Donec condimentum odio dui, id malesuada ipsum porta ac. Aliquam imperdiet cursus ullamcorper. Duis cursus tincidunt nibh sit amet cursus. Aliquam urna orci, sodales ac ipsum at, placerat lobortis neque. Sed sit amet convallis felis, vestibulum finibus arcu. Phasellus venenatis egestas felis, id aliquam turpis iaculis non. Proin a lacus ut felis malesuada sodales. Duis elementum, eros ac placerat bibendum, quam nisl varius mauris, vel venenatis neque augue et justo. Nunc varius sem velit, vel tempus mi aliquet id. Fusce purus massa, mollis id nulla non, dignissim cursus massa. Sed sollicitudin interdum felis, nec eleifend nisl feugiat eget. Nulla rutrum id odio ut consectetur. Maecenas fermentum turpis vitae libero ullamcorper suscipit. + +Vestibulum auctor lectus ligula, non sollicitudin odio maximus nec. Cras faucibus, felis sed suscipit tempor, risus lorem consectetur est, eget pulvinar nibh dui eu dolor. Vestibulum pellentesque, ex aliquam vulputate laoreet, libero lorem rhoncus risus, vitae congue velit justo vitae nisi. Nullam placerat venenatis tortor, sit amet lacinia augue placerat nec. Quisque vulputate lectus vel pulvinar condimentum. Nullam at libero iaculis, mattis purus vel, tincidunt diam. Suspendisse eu ullamcorper eros. Nulla id eros odio. Ut enim leo, ultricies quis mauris sed, interdum commodo felis. Etiam enim lacus, scelerisque a interdum eget, mollis vel tellus. Phasellus vel vulputate orci. Nulla ornare leo sed arcu rhoncus convallis. Duis libero arcu, pulvinar ut tellus vitae, aliquam euismod magna. Donec semper nisi eget nulla tempus, sed condimentum massa sollicitudin. + +Suspendisse bibendum ante vitae ullamcorper semper. Praesent dui est, elementum nec lacus in, pellentesque accumsan sapien. Cras quis urna at lacus posuere commodo eget nec arcu. Nunc varius ante quis ligula rhoncus, ut dignissim metus commodo. Integer volutpat gravida nunc eget faucibus. Quisque imperdiet, mauris vitae cursus malesuada, lacus mauris tempus sem, ut accumsan purus lectus quis nisi. Vestibulum aliquam dui sed nisl laoreet, id posuere quam imperdiet. Aliquam ultricies non enim vel tempor. Donec sed feugiat justo, vel rutrum enim. Phasellus lorem quam, dapibus a tincidunt vulputate, pellentesque non lorem. + +Nunc quam diam, condimentum sit amet enim semper, hendrerit laoreet magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam et pulvinar leo, ac fermentum lectus. Sed erat ante, mattis ac tempor vitae, euismod at tortor. Ut sagittis fringilla scelerisque. Ut pulvinar molestie leo eu faucibus. Sed quis efficitur purus. Pellentesque nibh nisi, porta id orci id, sagittis sodales tellus. Ut venenatis velit a lectus lacinia, at ultrices nisi commodo. Vivamus eros orci, ornare vel ullamcorper elementum, posuere id ligula. Aliquam non massa pellentesque, semper ipsum scelerisque, dapibus leo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla pretium leo diam, sit amet bibendum lacus tempus quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer quis pulvinar nibh. + +Mauris sed eros nisi. Cras malesuada, turpis vitae laoreet porta, sapien odio maximus nulla, ut efficitur mauris odio tincidunt elit. Nam ut urna eros. Sed at vestibulum risus. Nulla luctus pulvinar rhoncus. Pellentesque maximus ligula a est ullamcorper, sed tempus tortor ultrices. Curabitur ligula odio, mollis sit amet risus quis, tempor auctor magna. Suspendisse vel quam quis lorem commodo facilisis. Donec eu ipsum suscipit, molestie dui sed, fringilla dui. Proin placerat ex a lorem sodales convallis. Sed molestie quam mi. + +Fusce laoreet nec dolor id bibendum. Nunc condimentum vulputate massa lacinia fermentum. Donec maximus cursus eros sed porta. Nunc eu porta turpis. Nulla cursus et nisl eu elementum. Ut rutrum scelerisque aliquet. In tellus erat, rhoncus id tempus vitae, vestibulum non quam. + +Vivamus luctus elit a efficitur dapibus. Praesent magna mi, pellentesque sed accumsan dapibus, blandit at lectus. Praesent sodales erat diam. Pellentesque placerat lobortis enim, a dapibus ligula molestie ut. Phasellus dui augue, suscipit ac urna non, iaculis eleifend augue. Maecenas sed elit iaculis, pretium ligula lacinia, aliquam libero. Nulla sem mi, pellentesque viverra lorem vel, luctus vulputate augue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque fermentum nunc ex, at lobortis turpis congue vitae. Suspendisse iaculis elementum enim commodo facilisis. Vestibulum non neque tellus. Proin vitae sodales urna. Mauris interdum purus et neque tempus, vitae mattis libero finibus. Suspendisse iaculis condimentum nibh, eu mattis enim vehicula a. + +Fusce dictum urna non lectus ullamcorper vestibulum id in elit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc fermentum odio non ante sollicitudin posuere. Praesent vulputate quam nec magna euismod pellentesque. Etiam tempor nunc eget velit volutpat eleifend. Mauris sodales nunc ullamcorper, gravida purus eget, rutrum sem. Nam a sapien rhoncus, scelerisque lacus ac, condimentum ipsum. Pellentesque suscipit, ligula ut rhoncus ullamcorper, ligula augue rutrum lorem, eget aliquam risus tortor eget metus. Curabitur scelerisque bibendum purus vel vulputate. Morbi et odio at neque sodales suscipit. Nam at pretium nulla. In scelerisque vulputate suscipit. Nulla facilisi. Pellentesque eu sem eu ligula efficitur viverra. Pellentesque gravida consequat urna id bibendum. + +Nulla consectetur facilisis est nec aliquam. Proin hendrerit magna suscipit ante accumsan hendrerit. Nulla ut sem id neque tempor vehicula. Sed eget massa eget sem placerat tincidunt. Ut eleifend, justo sit amet egestas lacinia, ex velit pharetra nulla, nec aliquet elit neque ut turpis. Quisque in gravida velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed euismod malesuada convallis. Ut ultricies, magna et blandit fringilla, dolor dolor maximus sapien, in aliquet augue est vel odio. Nulla commodo elit massa, euismod tempor turpis aliquet eget. Morbi non odio et tortor egestas ultrices. Etiam semper, ipsum et dictum pharetra, eros purus bibendum lacus, vel laoreet lacus sem vehicula nibh. Nunc enim nulla, pulvinar sit amet lobortis sed, porttitor molestie risus. Morbi tincidunt diam mi, nec auctor sem rutrum at. + +Donec pellentesque odio odio, eu condimentum risus consectetur quis. Vivamus ullamcorper, mauris non semper rutrum, dui risus suscipit tellus, ut tempor velit risus vitae nibh. Duis tempor nibh at tristique rutrum. Cras congue nisl at sem sollicitudin efficitur. Aenean auctor purus vel libero fermentum elementum. Mauris convallis orci id interdum accumsan. Aliquam at metus risus. Sed accumsan, quam id dignissim congue, velit risus eleifend ligula, ut fringilla elit erat at neque. Aliquam tempor, quam ut ultrices volutpat, orci lectus vulputate turpis, eget pharetra purus nisi non lorem. Ut condimentum convallis justo, a volutpat eros. Sed tempus turpis leo, in convallis est fringilla sed. Vivamus eu laoreet tellus. Quisque ullamcorper ullamcorper leo. Nam fermentum egestas facilisis. Nulla egestas ligula feugiat urna molestie, faucibus convallis erat accumsan. Cras pellentesque ipsum lectus, vitae luctus dolor suscipit nec. + +Vivamus vel nibh sed mi tincidunt cursus quis facilisis tellus. Donec eget est eu velit pretium molestie. Maecenas lacinia risus turpis. Sed tristique id risus sit amet venenatis. Duis maximus, metus ac molestie convallis, quam ex dapibus sem, at rutrum metus nisi quis lectus. Suspendisse sodales in nisi at hendrerit. Maecenas suscipit lobortis vulputate. Nunc convallis sit amet elit eget porta. Mauris tincidunt massa in augue finibus iaculis. Vestibulum imperdiet, orci vel bibendum laoreet, ligula quam mollis lacus, a eleifend nisi tellus vitae ipsum. Cras non ante sollicitudin, auctor dolor id, condimentum tellus. Morbi malesuada leo nec lectus scelerisque, nec interdum lacus ornare. Integer ultrices ligula nunc, sed blandit urna aliquam eget. Proin consequat viverra ex non rhoncus. Phasellus id nibh at tortor sodales blandit. Donec dignissim ipsum vel ligula malesuada rhoncus. + +Nullam mauris sem, dapibus ac nisl at, hendrerit faucibus nisi. Mauris dictum fermentum pellentesque. Praesent in gravida odio. Vestibulum pharetra iaculis est quis tincidunt. Sed venenatis rhoncus lacus, non porta ante vulputate at. Duis fringilla ipsum at urna euismod molestie. Duis a porttitor ante. Mauris pharetra elit et metus auctor, a eleifend sapien porta. Vivamus ut tincidunt purus, lobortis euismod tellus. Nullam non nulla gravida, laoreet tellus ac, placerat urna. + +Sed justo sapien, scelerisque nec nisl vel, efficitur aliquet purus. Phasellus eget posuere nisl. Integer porta vel purus nec ornare. Pellentesque rutrum in nulla at hendrerit. Nullam elementum sodales volutpat. Duis tempus, purus sagittis auctor ullamcorper, dui erat hendrerit nulla, id finibus eros dui quis risus. Quisque posuere nunc quis augue placerat lacinia. Nunc quis lorem vulputate, tincidunt enim nec, rhoncus odio. Ut porttitor porta velit ut vulputate. Duis convallis sagittis magna nec gravida. Ut eu luctus sem, non placerat erat. Vivamus viverra ut ligula ac finibus. Cras ligula urna, tincidunt id risus eu, dapibus viverra lorem. Aliquam erat volutpat. Sed dolor nisi, faucibus non ligula faucibus, laoreet bibendum diam. Vivamus sagittis lacus ut condimentum tincidunt. + +Proin tristique mi ullamcorper dolor accumsan mollis. Nulla facilisi. Pellentesque iaculis mattis augue ac rutrum. Mauris in nulla at ligula fringilla pulvinar. Nam commodo ornare nibh ac viverra. Maecenas eget augue a risus mattis ullamcorper non at nibh. Aenean nec erat diam. Pellentesque augue nisl, venenatis mollis dolor non, bibendum malesuada leo. Pellentesque elementum purus ornare mauris iaculis porttitor. Sed mollis tempor dui eu elementum. Donec porttitor tellus non mattis gravida. Mauris venenatis suscipit convallis. Sed dolor sapien, pellentesque et tellus a, tempus cursus magna. Nunc lobortis leo magna, at maximus orci ultrices eget. Nullam vulputate dictum nisi, vel egestas orci. + +Nam nibh mi, ornare sit amet nibh vel, lobortis lacinia leo. Ut egestas mi vel nunc luctus vestibulum. Nullam id tempus felis, eget convallis erat. Aliquam venenatis ut tellus id fringilla. Aliquam erat volutpat. Sed vehicula, odio nec mollis dapibus, ligula sapien consequat risus, ut volutpat diam neque ut neque. Mauris venenatis libero vitae sem elementum, sit amet maximus massa varius. Maecenas malesuada mi id leo euismod, eu maximus sapien vehicula. Nulla facilisi. Duis nec venenatis ante, non pulvinar lacus. In sollicitudin sit amet velit suscipit egestas. Maecenas a lectus euismod, dictum nulla at, malesuada lectus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec lorem massa, laoreet non elit nec, sollicitudin blandit felis. + +Cras dignissim fermentum tellus ut fringilla. Maecenas maximus eros pretium sagittis venenatis. Maecenas id enim ac est semper efficitur ac hendrerit leo. Cras eu arcu tincidunt risus pellentesque aliquam. Pellentesque vel sodales libero, non rhoncus enim. Integer vulputate vulputate libero, eu consectetur eros euismod vitae. Fusce magna nibh, vehicula sed turpis eget, malesuada ultricies sem. Sed convallis sem nisl, rhoncus mollis mauris bibendum ut. Pellentesque vitae massa a justo dapibus laoreet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed mollis egestas nisl vel ultricies. Phasellus sit amet varius leo, sed vehicula sapien. Etiam in urna eget neque aliquet ultricies in nec quam. Aliquam at feugiat elit. + +Maecenas placerat nisi ultrices neque pulvinar, et ultrices ante tempus. Vestibulum laoreet quam lacus, eget commodo magna dictum consectetur. Aliquam erat volutpat. Aenean tincidunt ipsum sit amet justo aliquet tempus. Quisque blandit sit amet est facilisis dictum. Sed non consequat dui. Integer purus diam, gravida quis tortor lobortis, iaculis vehicula sem. Morbi pellentesque est elit, vitae interdum elit laoreet et. Vestibulum in blandit ante, eu blandit velit. Morbi sagittis eu est eu vulputate. Nullam ac mi feugiat nibh feugiat suscipit. Sed interdum tincidunt efficitur. Integer dictum sem erat, ut pharetra libero lacinia in. Mauris at hendrerit odio, a facilisis lacus. Donec blandit massa ac nisi ultrices faucibus. + +Aenean tempus id ligula at mollis. Cras id dui magna. Integer id neque tincidunt, rhoncus nunc et, laoreet nibh. Pellentesque consectetur odio ligula, et consectetur tortor scelerisque non. Cras quis ex pharetra mi ornare lobortis. Phasellus fringilla interdum felis, vel aliquam ligula. Mauris cursus varius turpis in iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent eget mi id nulla semper dapibus. + +Vivamus lacus augue, eleifend dignissim ipsum eu, interdum accumsan massa. Nam id quam vel ligula consectetur volutpat id eget eros. Aliquam sed felis velit. Duis egestas velit ac dolor blandit, ut elementum mi tincidunt. Integer varius nisl a sapien pellentesque, et pellentesque ante elementum. Fusce ac orci interdum, faucibus nisl ut, sagittis nisi. Vestibulum sed diam eu magna congue ornare. Integer dignissim ligula sit amet mauris pretium vulputate. Duis ac tincidunt magna. Sed vehicula, ante nec vulputate venenatis, mauris odio blandit dolor, vitae lacinia nibh lorem et metus. + +Sed non sapien vel lorem tempor semper ac ac tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas auctor ante sit amet suscipit vulputate. In ac cursus turpis. Donec eu sem bibendum, blandit est nec, blandit tortor. Pellentesque scelerisque justo vitae magna cursus, vitae egestas libero interdum. Pellentesque vitae ligula vel mauris vehicula convallis. Nunc ornare lectus sit amet lorem aliquet dignissim. Cras nec ligula pretium, semper nulla a, pulvinar lacus. Sed ac augue bibendum, posuere ipsum eu, venenatis quam. Sed elementum nunc quis odio pellentesque, a vestibulum ex congue. Cras id malesuada arcu. Mauris ut egestas nulla, sed tempus ligula. Donec ac elit ut nisi pulvinar elementum. Suspendisse id auctor lectus, vitae ultricies lacus. + +Vestibulum pulvinar ornare cursus. Curabitur accumsan sollicitudin mi in vestibulum. Vestibulum vulputate tincidunt luctus. Suspendisse potenti. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus a mattis neque, id malesuada odio. Aliquam id auctor magna. Vestibulum quis nibh lacinia, tincidunt felis sed, vestibulum ex. + +Donec placerat ipsum diam, vel imperdiet velit eleifend ac. Quisque dapibus erat non dui convallis eleifend. Praesent pellentesque felis id suscipit rutrum. Donec quis lacinia turpis. Nulla justo eros, fringilla vel fringilla in, euismod sollicitudin arcu. In ut lobortis arcu, at rhoncus elit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Pellentesque iaculis quam nec interdum eleifend. Fusce mauris mauris, imperdiet sollicitudin volutpat eu, sollicitudin blandit leo. Vestibulum hendrerit diam gravida erat imperdiet, blandit dignissim dui tempor. Phasellus viverra ante quis aliquam finibus. Duis sed condimentum augue, nec sollicitudin dui. Ut ullamcorper metus ac ligula accumsan, ac fringilla metus gravida. Morbi rhoncus est nunc, at lacinia ex fringilla sit amet. Integer lobortis ultricies nisi vitae accumsan. Nulla nec sagittis ante. Mauris bibendum nisi ut magna vulputate maximus. Praesent in elit eu metus dignissim cursus. Ut a tellus felis. Mauris eget ornare diam. + +Suspendisse potenti. Nam nec tortor ex. Duis eu elementum ligula. Pellentesque efficitur sodales orci, vitae sollicitudin odio tempor ac. Nam lacus ante, lobortis vitae lobortis eu, eleifend et velit. Nulla et lectus eu nibh tristique malesuada a sit amet felis. Proin molestie, lectus vitae sagittis malesuada, massa velit finibus est, id vestibulum dolor felis eget lectus. Phasellus varius pellentesque neque, a malesuada ex mattis eget. Nulla facilisi. Phasellus interdum placerat lobortis. Sed et odio tristique, viverra libero et, sagittis diam. Proin tristique lectus id bibendum maximus. Integer ornare euismod ligula nec malesuada. Nulla facilisi. Nullam bibendum hendrerit pharetra. + +Duis pharetra auctor felis, eget aliquet justo commodo at. Donec quis nibh non arcu sodales efficitur. Aenean lorem sapien, tincidunt eu sapien nec, convallis tempor diam. Curabitur volutpat, felis ut congue euismod, lacus nisl euismod ipsum, vel consectetur orci nibh sed ligula. Curabitur consectetur vitae mauris sed tempor. Fusce vel pretium nibh, et tristique dolor. Aliquam semper a ante non finibus. Praesent euismod urna augue, at feugiat orci commodo ut. Duis imperdiet purus non augue cursus gravida. Maecenas sodales purus et sollicitudin venenatis. Nam ultrices lorem lectus, ut pretium turpis hendrerit eget. Vestibulum vel lacinia lectus, at dignissim diam. Vivamus ut tortor ac tortor blandit finibus. Etiam porttitor tortor sit amet elit lacinia gravida. Pellentesque pretium, orci vitae tempor vehicula, nisi tellus tincidunt sapien, id egestas felis quam a nunc. Pellentesque sed vestibulum nisl. + +Mauris congue, justo vel dapibus pretium, ipsum augue consectetur leo, at aliquam ipsum neque non mauris. Nam sollicitudin in urna eu blandit. Aliquam erat volutpat. Morbi eget interdum ante. Pellentesque congue gravida arcu, eget ornare ante elementum nec. Integer a auctor dolor, in varius sem. Etiam dictum nibh magna, at volutpat nunc blandit eu. Curabitur at sem ipsum. + +Nulla porttitor erat eget volutpat iaculis. Pellentesque egestas, nisl vel ultrices efficitur, ex magna condimentum urna, in tincidunt massa turpis eget metus. Etiam vitae magna elementum, fermentum justo ut, pretium velit. Aliquam aliquet venenatis malesuada. Quisque consequat enim metus, pellentesque blandit leo rhoncus id. Vivamus vestibulum, est in condimentum mollis, ligula eros blandit lorem, vitae dignissim lectus mi eget nulla. Cras posuere leo at neque convallis, sed pretium massa ultrices. Morbi tempus porttitor turpis. Nam vitae mauris et massa venenatis tincidunt. Quisque vestibulum lacus ligula, in sodales felis elementum vel. Sed a tellus condimentum, sodales nisi id, aliquet elit. + +Vestibulum ac ex eu turpis lacinia condimentum nec eget ipsum. Nulla non imperdiet erat, at malesuada lorem. Praesent efficitur imperdiet augue vel laoreet. Sed dignissim, ante non porta feugiat, enim nunc rhoncus lorem, eu luctus turpis risus sit amet orci. Vivamus bibendum pharetra venenatis. In at gravida nisi. Vestibulum pulvinar sapien ac augue scelerisque, vitae blandit nibh malesuada. Nunc vitae est faucibus, accumsan risus a, laoreet urna. Cras imperdiet lacus in augue facilisis dignissim. Duis sed nisi quis diam mollis rutrum. + +Vestibulum eget egestas neque. Praesent viverra, velit quis porttitor euismod, sem libero venenatis mauris, id congue urna nulla sed lorem. Nulla mollis metus nec diam malesuada aliquam. Duis ac risus nunc. Cras sollicitudin urna nunc, id sodales quam gravida sit amet. Fusce in vulputate orci, in venenatis lorem. Donec a sagittis ipsum. Quisque consequat sapien tellus, sed efficitur lacus aliquam eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean in neque at augue elementum commodo pellentesque eget ligula. Nullam condimentum efficitur tincidunt. Phasellus posuere tincidunt odio sed facilisis. Aenean eu risus at est euismod facilisis. Curabitur elit purus, malesuada quis blandit id, rutrum vitae dui. Praesent porta rutrum erat, ullamcorper viverra nunc. Cras ut velit dui. + +Etiam posuere pulvinar mi at ullamcorper. Pellentesque finibus, tellus non convallis commodo, orci nibh dapibus nisl, at aliquam purus nulla eget dui. Praesent fringilla urna nec nulla pellentesque, nec rhoncus turpis ultricies. Sed laoreet velit pellentesque libero varius, ac interdum urna viverra. Phasellus sed consectetur massa. Morbi quis velit nec ipsum varius tempor. Proin id sodales felis. Aliquam lacinia risus quis ligula condimentum sodales. Nulla vel arcu aliquet neque iaculis aliquet. Cras sed lorem eu turpis tincidunt sodales. Sed pulvinar elementum ligula, nec faucibus nisl. Fusce nec tellus eget dui tempor sagittis. In vitae enim in ex viverra commodo. Duis est erat, fringilla ac semper a, dapibus in tortor. + +Maecenas commodo vulputate iaculis. Aliquam non facilisis est. Donec pellentesque vitae nibh nec volutpat. In commodo metus placerat lorem commodo, non lacinia nibh bibendum. In viverra rhoncus erat. Mauris nec nisl blandit, elementum justo nec, accumsan libero. Aliquam elementum, velit et ullamcorper convallis, turpis lorem elementum lorem, quis consequat tortor eros ut erat. Curabitur et enim quis felis vulputate congue et vel purus. Sed elementum interdum ipsum, sed tincidunt arcu scelerisque et. + +Aenean interdum elementum mauris ut porta. Mauris vel purus ac odio vulputate pulvinar at quis odio. In turpis turpis, convallis in augue id, elementum vulputate lorem. Sed tincidunt fermentum vulputate. Nunc ipsum ipsum, molestie vel convallis id, pharetra vel arcu. Maecenas vel dui elit. Sed blandit dolor sit amet risus commodo faucibus. Duis rhoncus felis arcu, vel aliquam nisi faucibus sed. Suspendisse cursus eget nunc ut bibendum. Fusce non ligula risus. Curabitur vitae cursus metus, quis fringilla diam. Phasellus turpis ante, pulvinar ac turpis tristique, sollicitudin congue lectus. Proin quis ipsum at ipsum euismod euismod. + +Nunc ut fermentum nunc. Donec id commodo lacus, at hendrerit justo. Donec cursus purus sodales nunc commodo, quis bibendum elit hendrerit. Quisque quis pellentesque nibh, ac vulputate neque. Aenean non placerat felis, eget feugiat ligula. Vivamus a eros accumsan, cursus neque at, ultricies magna. Fusce tincidunt tellus vitae mi rutrum laoreet sed quis ligula. Nullam ullamcorper ligula ligula, vel fringilla metus aliquet a. Morbi aliquet, mauris vel interdum venenatis, ex arcu venenatis tortor, at tincidunt dui ipsum et arcu. Nulla blandit gravida nulla ac iaculis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ullamcorper lectus in sapien lobortis, eget posuere massa varius. + +Cras lacinia nunc faucibus mauris placerat pretium eget non sapien. Morbi viverra bibendum posuere. Aliquam accumsan sagittis dolor non iaculis. Sed nunc odio, lobortis in dolor ac, rutrum fermentum velit. In venenatis, velit in molestie semper, est magna condimentum dui, aliquam auctor lorem nulla vel ligula. Morbi vehicula turpis turpis, quis mollis justo tempus vitae. Proin luctus lacus in mauris porttitor aliquet. Duis vitae nunc ex. Nullam eget erat vitae nulla iaculis rhoncus. Sed lacus dui, suscipit eu leo vitae, tincidunt dignissim risus. Praesent ut massa ut arcu sagittis consequat. Sed sit amet tincidunt turpis. Nulla bibendum, felis eget posuere dictum, libero mi tristique elit, at venenatis neque elit ac quam. Curabitur nisi sem, scelerisque nec nunc ac, pulvinar pharetra odio. Curabitur egestas pellentesque arcu sed suscipit. In mattis dolor vel dui mollis feugiat. + +Sed commodo, dui ac vestibulum dictum, tellus libero tincidunt lacus, viverra commodo est felis vitae urna. Proin tincidunt neque vel turpis eleifend laoreet. Vestibulum sagittis, tortor sed iaculis consequat, urna ante sagittis est, ac ullamcorper lorem nibh dignissim odio. Nam arcu mi, cursus et blandit nec, aliquam ut nulla. Aenean quis iaculis tellus, eu egestas augue. Etiam pretium eget nisi quis iaculis. Aliquam sed convallis eros. Ut bibendum rhoncus lacus, in vestibulum dui ultricies id. Fusce vestibulum, mauris ut tempor consequat, dolor nisl pellentesque elit, in porta arcu elit vel ante. Vivamus at nisl est. Etiam nec blandit tortor, at pulvinar orci. Proin semper dapibus tincidunt. Phasellus lobortis enim ullamcorper dolor tempor cursus. + +Mauris a libero in enim gravida aliquet ac sit amet nibh. Vestibulum ac neque posuere, blandit libero ac, vehicula enim. Aliquam auctor iaculis eros sit amet molestie. Quisque faucibus turpis et massa tristique, nec dapibus mauris aliquet. Proin blandit aliquet mauris, non tincidunt odio blandit vel. Curabitur a nibh in eros commodo tincidunt eu et libero. Curabitur sit amet dapibus ex, in condimentum magna. Sed eu sem sem. Nunc tellus dolor, rutrum eu mauris nec, congue feugiat purus. Fusce tempor, neque vitae bibendum imperdiet, dolor ipsum condimentum urna, et egestas quam tortor in ex. Aliquam velit magna, commodo hendrerit sagittis sed, feugiat eget erat. Nunc quis ullamcorper velit, eu consequat augue. Nam arcu mauris, condimentum sit amet magna in, finibus scelerisque nunc. Mauris erat est, hendrerit ac accumsan eget, facilisis ut nisl. Quisque dignissim arcu quis diam tincidunt tristique. Sed rhoncus nisl non enim fermentum, a lobortis dolor consectetur. + +Sed eget condimentum ligula. Vestibulum vitae cursus eros. Donec elementum sapien magna, posuere iaculis sem ultrices lobortis. Morbi eu bibendum lectus. Suspendisse ante eros, ullamcorper ac viverra eget, pellentesque sed sapien. Duis sit amet tincidunt dui, vitae lobortis purus. Sed venenatis tincidunt volutpat. Vivamus a nisl ac elit consectetur semper ut eu libero. Proin id cursus ex. In hac habitasse platea dictumst. Aenean sed nisi vitae odio venenatis pulvinar vitae ac risus. Sed varius magna ut erat luctus vehicula. + +Nunc non ex eget purus blandit faucibus at quis velit. Donec quis mi vestibulum, facilisis nisi quis, tincidunt turpis. Sed bibendum metus sed consectetur mollis. Maecenas fermentum, erat finibus pulvinar lacinia, ex risus dictum sem, ut vestibulum augue diam vel diam. Donec ac massa non nibh pretium laoreet eget in orci. Nulla placerat eleifend mi, pretium vestibulum diam condimentum vitae. Nunc odio turpis, feugiat vitae turpis eget, pellentesque commodo turpis. Etiam sapien purus, consequat nec mi eget, consectetur efficitur neque. Phasellus porttitor sapien sit amet nunc semper, vel bibendum nibh finibus. Ut ac imperdiet ex, eu congue felis. In posuere nisi felis. Mauris tempus pretium mauris, ac viverra nunc hendrerit id. Sed fermentum nec sem ac pulvinar. Integer dictum velit eget congue venenatis. + +Cras eros dolor, venenatis ac dictum sed, dignissim nec sem. Curabitur tempor erat quis pretium interdum. Nunc vestibulum justo nisi, sit amet sagittis tellus consequat vel. Donec pharetra nunc vitae consequat eleifend. Quisque ut mauris quis nunc volutpat consectetur. Nam a suscipit ligula, at gravida libero. Vestibulum blandit, tellus sed bibendum volutpat, libero tortor convallis nisl, sit amet placerat lorem dolor nec libero. Integer blandit libero elit, ut congue ex euismod congue. Nulla sodales justo id eros condimentum faucibus. + +Proin quam ante, hendrerit at bibendum eu, pharetra at lectus. Proin finibus arcu id nisl aliquam dapibus. Fusce a suscipit nisl. Ut placerat ultrices nibh nec efficitur. Vestibulum vitae interdum magna. Donec lobortis finibus risus, et luctus ipsum efficitur euismod. Quisque dictum diam et venenatis euismod. Cras dictum molestie aliquet. Vestibulum imperdiet quam eget diam malesuada, quis pulvinar odio dignissim. Curabitur sapien elit, iaculis vitae justo eget, pretium malesuada elit. Donec gravida molestie tincidunt. Nullam at commodo ipsum. Sed nisi erat, tincidunt ultricies interdum consequat, pretium mollis ipsum. Vivamus in erat consectetur, sodales nibh at, porta nunc. Mauris semper, dui vitae condimentum tempus, mauris justo volutpat urna, ac ullamcorper tortor dolor in sem. + +Aenean leo ligula, egestas at vestibulum ac, porta vel mauris. Curabitur at lorem non mauris fringilla venenatis vel eu arcu. Donec posuere eleifend diam. Duis aliquet justo lacus, eu egestas erat eleifend sed. Sed non cursus urna. Sed pretium purus id blandit varius. Mauris turpis mi, lobortis eu tellus sit amet, maximus venenatis felis. Proin non varius enim, aliquet finibus velit. Praesent posuere pharetra ipsum eget varius. Phasellus non fermentum magna, ut iaculis augue. Praesent ut nisl nunc. + +Sed ligula tellus, interdum at sapien ut, dictum pellentesque nisl. Duis fringilla, sem nec elementum dapibus, nisl tellus maximus velit, eu varius sem nisl feugiat eros. Maecenas quis viverra lacus. Nulla nec commodo ex, nec placerat enim. Curabitur ultrices sagittis fringilla. Vestibulum sit amet enim sagittis, condimentum nisl sit amet, pulvinar orci. Ut at erat finibus erat bibendum convallis. Quisque euismod magna eget leo facilisis hendrerit. Cras venenatis, nisi quis sollicitudin volutpat, metus enim vestibulum nunc, at mollis leo leo nec est. Fusce fermentum tristique feugiat. Suspendisse sem est, condimentum ut ante at, egestas convallis ante. Vestibulum dictum, ex nec imperdiet laoreet, magna est ullamcorper augue, vel molestie quam odio vel ante. Donec dui dui, posuere a neque ac, condimentum vehicula magna. Curabitur suscipit, risus vitae bibendum posuere, mauris lorem viverra est, at tristique arcu quam quis leo. Donec sed posuere neque. Suspendisse iaculis aliquet condimentum. + +Morbi tempus condimentum diam eget bibendum. Aliquam varius magna quis lectus interdum, in elementum ligula tempor. Morbi vitae lorem sapien. Morbi luctus consectetur eros non aliquet. Pellentesque vestibulum sem sed ante accumsan faucibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ac suscipit justo. Nunc efficitur lectus eu arcu venenatis, vel accumsan ex suscipit. Vestibulum egestas ultricies tellus, et interdum enim pretium eu. Aenean rutrum est tincidunt rhoncus molestie. Phasellus hendrerit tellus et laoreet varius. Integer efficitur felis magna, nec sollicitudin arcu sollicitudin in. Curabitur non feugiat odio. Mauris nisi odio, luctus quis dolor sed, tristique luctus ex. Nullam libero enim, facilisis ut venenatis et, vulputate sed purus. + +Mauris a odio ut leo porttitor ultrices a et ex. Pellentesque vestibulum lacinia faucibus. In pellentesque eget augue at feugiat. Integer finibus augue dolor, in luctus lorem rutrum vitae. Donec pharetra lectus ac purus sollicitudin, vel tristique mauris pretium. Cras porttitor mi eu lectus consequat, a ultrices felis venenatis. In condimentum turpis in velit mattis laoreet. Aliquam sed mauris id nulla ultrices convallis vel vel velit. Suspendisse ut arcu finibus justo fermentum fringilla. Etiam in malesuada nisl. In hac habitasse platea dictumst. Duis a est lacinia, pretium dui eget, condimentum turpis. Integer ac placerat augue. Donec et eros felis. + +Integer cursus magna id quam sagittis consectetur. Aliquam erat volutpat. Quisque ullamcorper nisl nec massa dapibus facilisis vitae at nunc. Donec laoreet, libero in elementum tempus, enim odio porttitor felis, venenatis fermentum augue velit eu urna. Ut at ullamcorper enim. In molestie, velit et blandit maximus, erat nunc laoreet quam, vel finibus est mauris non sapien. Donec et dictum lorem. Nunc vestibulum, dolor eget tempus maximus, elit eros aliquam velit, vitae mattis mauris lectus ac tortor. Donec rutrum justo orci, id dictum metus vestibulum a. Etiam bibendum ipsum convallis, lacinia turpis vitae, dictum tortor. In velit dolor, scelerisque sed molestie nec, volutpat a turpis. Cras posuere commodo erat ut gravida. Quisque ante ipsum, volutpat a tellus non, dignissim ornare elit. Pellentesque sed porttitor dui, luctus rhoncus purus. In vitae ante pulvinar, consectetur orci quis, tempus velit. Curabitur tempus ligula id sapien ornare rhoncus. + +Maecenas eu nibh et elit accumsan blandit a at erat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam commodo feugiat condimentum. In non ante ut mauris eleifend lobortis. Phasellus eleifend vitae metus et semper. Nam sit amet rhoncus diam. Nunc molestie libero sed erat volutpat consequat. + +Aenean eget tristique odio. Vivamus quam tellus, dignissim sed faucibus sed, sagittis ut elit. Maecenas in ullamcorper sem. In at ipsum accumsan lectus vestibulum commodo nec non leo. Aliquam at suscipit felis. Nunc non egestas tortor. Donec sit amet eleifend eros. + +Sed eleifend nisi velit, in egestas erat condimentum eu. Pellentesque a tincidunt urna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum dapibus mauris vitae elit auctor, id venenatis sem consectetur. Vivamus non leo pulvinar, blandit libero ut, vehicula arcu. Nullam elementum ex enim, at mattis massa pharetra eu. Nunc nulla magna, lobortis a magna sit amet, laoreet fermentum justo. Curabitur aliquam sollicitudin posuere. Aenean semper porta dictum. Mauris accumsan non nisi nec faucibus augue. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in nibh eget ante viverra mattis. Etiam elit est, pharetra efficitur fermentum at, accumsan id eros. Aenean accumsan nisi facilisis tempor suscipit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam magna erat, tincidunt eu placerat at, molestie at sem. In sit amet cursus mauris. Etiam ullamcorper lacus nisi, et porta metus semper id. Nulla mauris nunc, pharetra at facilisis a, consequat id metus. Sed convallis ligula non felis vulputate finibus. Curabitur nisl nulla, pharetra in justo a, dapibus ultricies dui. Morbi ultricies sollicitudin purus, quis pellentesque leo rhoncus sed. Curabitur sed eros quis lacus vulputate pretium. Vestibulum vitae urna nec arcu vulputate efficitur. Cras venenatis sodales dolor non ullamcorper. + +Donec eu placerat magna. Vestibulum justo lacus, luctus ac luctus sit amet, dapibus at orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec eu tortor ac justo pharetra hendrerit. Phasellus ornare lacinia vestibulum. Maecenas diam orci, molestie rhoncus fringilla vitae, aliquam eu purus. Maecenas vehicula felis dui, vel dapibus odio lobortis quis. Cras a velit non nisl efficitur tempor. + +Suspendisse magna enim, ultrices et elementum ut, venenatis ut purus. Curabitur convallis vel tortor id rhoncus. Pellentesque varius arcu non imperdiet eleifend. Vestibulum fringilla aliquet vehicula. Nullam et lorem ullamcorper, tincidunt ante eget, egestas ex. Donec laoreet, justo id tincidunt egestas, lectus diam faucibus tortor, nec venenatis felis leo a urna. In at tortor semper augue finibus ornare et vitae erat. Praesent vulputate, dui sed pulvinar porta, justo tortor feugiat tellus, eget accumsan velit turpis at orci. Nulla vitae velit facilisis augue sagittis fringilla nec sagittis nunc. + +Duis vel elementum odio, id eleifend mauris. Nam in ultrices nulla. Quisque pharetra blandit risus eget lacinia. Nulla mattis mi felis, a ultrices nisi egestas vulputate. Donec a augue ac ex laoreet semper at porta ante. Vestibulum arcu ipsum, vehicula vel mollis lobortis, ullamcorper sed augue. Ut viverra faucibus nunc, sit amet sodales diam gravida sollicitudin. Fusce finibus quam sed ornare consequat. Suspendisse viverra ultricies dui in volutpat. Maecenas blandit erat in rhoncus placerat. Phasellus eu nisi rutrum, bibendum nunc eu, viverra ex. Praesent sollicitudin mi sit amet dictum convallis. Vivamus purus ligula, interdum at tincidunt sit amet, pellentesque ut elit. Aenean vitae ultrices ex. In facilisis lectus est, nec vulputate diam tristique vel. In gravida tincidunt ex et viverra. + +Pellentesque mi justo, dignissim a nisl quis, tempor dictum lacus. Nam tristique varius dolor tincidunt pharetra. Nullam iaculis est sed quam dignissim cursus. Duis sit amet vehicula nisi, sed consectetur velit. Nullam non tellus nec dolor venenatis porta quis at quam. In pharetra id orci sed faucibus. Cras et malesuada erat. + +Quisque fringilla commodo metus nec feugiat. Donec et est urna. Maecenas ut magna dui. Vivamus eu sem venenatis, porta mi at, efficitur tortor. In lacinia hendrerit elit, in faucibus nulla ultrices et. Mauris accumsan nec orci et vestibulum. Aliquam eget justo velit. Mauris fringilla ullamcorper enim, in elementum enim eleifend a. Nulla id libero ac arcu tristique ultrices. Maecenas mattis orci vitae nisl laoreet auctor. + +Quisque id aliquam orci. Nunc molestie vitae quam eu consectetur. Vivamus molestie venenatis est, nec lacinia odio faucibus eget. Etiam ornare eu leo eget posuere. Quisque elementum ligula at euismod placerat. Maecenas lobortis leo diam, vel egestas odio iaculis ac. Integer dapibus mi metus. Sed at eleifend ligula, ullamcorper vestibulum eros. Suspendisse dignissim metus in vulputate iaculis. Nam rhoncus felis sit amet velit scelerisque semper. Ut sed augue eget nulla laoreet scelerisque. + +Aenean convallis ipsum id turpis gravida, et elementum ante facilisis. Donec vitae arcu id mauris mollis hendrerit. Mauris imperdiet cursus nibh at laoreet. Sed sit amet enim magna. Mauris blandit nisi quis arcu sodales, eget consequat orci euismod. Curabitur id bibendum diam. Ut pharetra tellus nec enim tristique, id efficitur eros accumsan. Suspendisse potenti. Praesent vel porttitor risus. Nulla vitae ex nec ex hendrerit euismod non mattis arcu. Aenean eget velit massa. Pellentesque venenatis arcu mauris, sit amet scelerisque sem lobortis ornare. Vivamus auctor porta eros, eget blandit lorem semper non. Nunc sed nibh commodo, hendrerit nunc at, malesuada nisl. Aliquam pretium leo sed iaculis vehicula. Vivamus interdum porta augue. + +Suspendisse potenti. Aenean sodales vehicula erat, vel sollicitudin eros eleifend id. Suspendisse hendrerit malesuada est, imperdiet tristique ex aliquet ac. Vivamus ultricies placerat lorem, ac placerat lectus sollicitudin ac. Etiam consequat odio vel bibendum pretium. Integer elementum nisl magna. Nam et vehicula risus, sed mollis tortor. Ut in molestie turpis. Nam luctus, elit molestie varius luctus, lacus ligula ullamcorper elit, non interdum ipsum neque non nibh. Curabitur vulputate facilisis suscipit. Sed vitae augue eu ex luctus lacinia ac vitae quam. Quisque non nibh ex. Praesent gravida efficitur dui, a vulputate nulla ultrices vitae. Duis libero dolor, facilisis in tempus vitae, pharetra non libero. Sed urna leo, placerat quis neque at, interdum placerat odio. Interdum et malesuada fames ac ante ipsum primis in faucibus. + +Vivamus in hendrerit elit, quis egestas sem. Sed tempus dolor finibus massa ultrices, sed tristique quam semper. Pellentesque euismod molestie facilisis. Vivamus sit amet ultrices elit. Ut eleifend, libero eu molestie maximus, ipsum elit pulvinar tortor, et aliquet nulla orci eu est. Nullam pretium, ligula at faucibus gravida, velit mauris pulvinar felis, sit amet tincidunt magna dui ut quam. Phasellus porttitor eu nunc id maximus. Suspendisse volutpat suscipit porta. + +Integer dictum augue purus, sed cursus eros blandit id. Vestibulum non nibh viverra, molestie lectus eu, vestibulum justo. Nullam a libero non nibh dignissim posuere id vel orci. Nulla viverra lorem eget condimentum scelerisque. Donec porta nunc sed sapien pretium dapibus. In hac habitasse platea dictumst. Suspendisse sit amet ligula elementum, accumsan ipsum vel, imperdiet massa. Fusce scelerisque non erat ac bibendum. Pellentesque consequat vehicula euismod. Morbi sapien nisl, ultrices ut scelerisque consectetur, ornare et orci. Maecenas efficitur eros a venenatis rutrum. Aenean bibendum dui in enim luctus posuere. Donec placerat porta eros eu dignissim. Fusce nulla velit, sodales eget nibh gravida, tincidunt venenatis urna. Aenean condimentum, massa in fringilla lobortis, turpis felis lacinia metus, sit amet facilisis nisl quam aliquet diam. Praesent fringilla vitae arcu nec lobortis. + +In tincidunt nisi ac dictum cursus. Sed dolor purus, posuere vel dolor vel, semper tempor elit. Integer nec scelerisque tellus, sit amet dictum magna. Donec hendrerit aliquet libero, a varius velit dapibus quis. Donec lectus turpis, egestas vel vestibulum vitae, iaculis quis eros. Maecenas id convallis metus. Duis quis tellus iaculis, lobortis massa vitae, condimentum eros. Pellentesque scelerisque nisl id hendrerit tempor. Donec quam augue, maximus eu porta ut, venenatis a ante. Curabitur dictum et nibh sed auctor. Nam et consequat odio. In vitae magna a dui porta pellentesque quis id magna. Sed eu nunc bibendum, imperdiet nunc sit amet, cursus diam. Vivamus in odio vel nibh sollicitudin molestie. Morbi sit amet leo et odio congue pharetra. Aliquam quis suscipit orci. + +Donec at ornare orci. Suspendisse nec tellus elit. Nam erat urna, laoreet at elit sed, tristique interdum ligula. Nullam dapibus orci sed lorem convallis fringilla. Cras urna ipsum, tristique maximus nisl quis, auctor mattis quam. Donec finibus consectetur lectus et interdum. Aenean posuere lectus vel turpis auctor finibus. Praesent molestie lectus ipsum, et tristique quam porttitor ac. Suspendisse aliquam pretium tortor, id egestas erat. Nulla mollis, orci at semper vulputate, nisl est pharetra diam, sit amet vulputate diam augue a sem. Donec in mauris felis. + +Nunc eleifend at elit a volutpat. Integer semper quis quam at faucibus. Donec pretium purus vitae erat pretium posuere. Sed ac aliquet nulla. Nam ut sagittis mauris. Sed quis sagittis risus, id pretium urna. Nam sagittis elementum ornare. Aenean ligula sapien, congue eget mi quis, viverra commodo nibh. Suspendisse non iaculis magna, nec volutpat neque. Donec eu dapibus eros. + +Nam sit amet iaculis massa. Nullam vel laoreet tellus, id cursus tortor. In hac habitasse platea dictumst. Curabitur in urna risus. Proin dui sem, interdum accumsan cursus ut, dignissim in purus. Nunc vitae consequat arcu. Proin volutpat elementum quam in posuere. Pellentesque luctus arcu in ornare tempor. Cras est turpis, viverra vel consectetur ac, dictum a quam. Donec sagittis tortor sed volutpat accumsan. Curabitur a tellus vitae arcu pretium viverra vel in ligula. Pellentesque turpis augue, porta vitae quam id, fermentum congue leo. Sed nec fermentum turpis. Nulla vehicula vulputate sem eu consequat. In tincidunt nisi et mi maximus, non facilisis justo porttitor. Proin eu sapien aliquam, accumsan nunc non, pulvinar leo. + +Nam vitae commodo sem. Ut laoreet in quam in suscipit. Nullam at faucibus diam. Fusce ut purus at ex lobortis elementum vitae quis sapien. Nam tempus consectetur ex, sed luctus felis. Donec porttitor mi dui, non luctus quam consectetur eu. Ut a egestas diam. Etiam sodales, nisl vitae gravida pulvinar, libero est condimentum tellus, vitae ullamcorper tortor justo a urna. Maecenas ac velit accumsan, laoreet leo eget, elementum libero. Maecenas dictum tincidunt blandit. Proin sed bibendum urna. Phasellus fermentum tincidunt tempor. Mauris iaculis, mi ac fermentum interdum, nibh odio pellentesque nunc, vel scelerisque sapien sem id purus. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas imperdiet sit amet lectus at hendrerit. Vivamus luctus, elit non lacinia hendrerit, risus velit finibus nulla, quis sagittis ipsum diam ut odio. Sed mauris sapien, vehicula efficitur gravida sed, gravida in lectus. Fusce eget consectetur turpis, in fermentum neque. Nullam turpis turpis, feugiat a accumsan in, euismod a augue. Vestibulum nec neque libero. Phasellus eget efficitur turpis, fermentum molestie nunc. Sed consequat elit urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Nunc egestas consequat blandit. Donec quis porta sapien. Sed augue nunc, efficitur vitae tellus sed, finibus bibendum sem. Fusce id sem quis quam pharetra ultrices. Phasellus non convallis velit. Quisque erat tellus, pretium eget porta in, ornare a arcu. Aenean nec lectus lorem. Suspendisse dolor lectus, ullamcorper ornare lorem a, consequat lobortis elit. Nunc dignissim est nec gravida facilisis. Proin faucibus erat vel eros volutpat, in vulputate neque sodales. + +Nunc vitae imperdiet ipsum, faucibus vehicula magna. Nulla nec nisl sapien. Curabitur et dui eget tortor efficitur accumsan. Aenean in pellentesque erat. Nam condimentum neque pulvinar, aliquam nisl eu, mollis lorem. Integer rutrum arcu eget felis semper euismod. Quisque vestibulum vel diam a tempor. Aenean mollis, tellus sit amet pretium pulvinar, nulla dolor ornare ligula, eget dignissim orci tortor ut sapien. Nam ut ipsum id lectus venenatis tempus vel non ex. Suspendisse blandit vitae nunc vitae porta. Suspendisse tincidunt est sit amet ultricies consectetur. Nulla fermentum hendrerit ex, vehicula rhoncus arcu lobortis ut. Vestibulum fermentum ornare diam at pellentesque. Praesent nunc lorem, porta et magna nec, sodales commodo justo. Duis aliquam sapien et rutrum tempus. Vestibulum malesuada felis eu ligula posuere luctus. + +Maecenas lacinia, lectus eget rhoncus aliquam, tortor est gravida sapien, vel aliquam arcu erat et magna. Praesent fringilla leo eget neque posuere imperdiet. In porttitor elit non enim gravida euismod. Aliquam tempus, orci at interdum dapibus, mauris lacus egestas sapien, ac ullamcorper ex nibh sed orci. Quisque iaculis enim et lectus egestas, malesuada posuere lectus interdum. Sed dignissim neque vel turpis dictum ornare. Vestibulum suscipit consequat maximus. + +Ut et ante sit amet leo rutrum volutpat. Sed malesuada quis sapien et ornare. Aliquam ac ex enim. Curabitur vel quam et orci posuere feugiat. Pellentesque nec metus eget sapien eleifend tincidunt non sit amet arcu. Cras posuere metus eget risus varius fermentum. Nullam orci eros, efficitur nec sapien nec, pretium laoreet erat. Nam gravida purus mauris, ac viverra orci hendrerit sed. Aenean ligula massa, posuere id faucibus vitae, malesuada quis augue. Morbi consectetur mattis mi, quis sodales diam bibendum eget. Aliquam sagittis neque at feugiat posuere. Donec gravida lectus a lectus fringilla tincidunt. Vivamus volutpat dui et turpis condimentum, ut tincidunt tortor lacinia. Ut laoreet, ante in pellentesque sodales, elit mauris scelerisque dui, quis tincidunt quam massa ac enim. Nulla porta porta sapien vel sollicitudin. + +Phasellus sed lectus posuere, mollis ante non, feugiat odio. Aenean a quam id mi ornare dapibus. Donec venenatis ipsum non velit accumsan, id elementum dolor imperdiet. Phasellus lacinia erat diam, sit amet convallis lectus ornare in. Curabitur lorem ligula, maximus eu varius quis, auctor quis odio. Etiam in orci porttitor, sodales ipsum at, rhoncus turpis. Nulla eget luctus nisi. Duis convallis est eget ligula fringilla viverra. Duis nec sapien quis dui luctus ornare sit amet quis erat. Quisque nec justo dui. + +Sed eleifend, tellus quis laoreet rhoncus, leo turpis imperdiet turpis, pretium varius odio tortor et leo. Morbi ut mi fringilla, porttitor sem ac, accumsan ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum cursus dolor accumsan dolor aliquet dignissim. Duis sed feugiat erat. Donec sed arcu accumsan, ornare nisl eget, lobortis nibh. Praesent pulvinar quis justo et hendrerit. + +Sed velit nibh, efficitur ut leo sed, eleifend iaculis nisl. Mauris ac diam euismod, luctus enim maximus, tincidunt sem. Ut tempus magna vitae blandit bibendum. Sed sapien tortor, ultrices et justo vel, pharetra faucibus dui. Vivamus et vestibulum arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris tincidunt suscipit risus, vitae varius felis commodo in. Nullam semper tortor dolor, sit amet pharetra odio interdum hendrerit. Pellentesque sed justo eros. Cras quam purus, eleifend id tellus molestie, eleifend finibus lorem. Donec a volutpat libero, at vehicula tellus. Vivamus tellus orci, pharetra in nisi vitae, tincidunt dapibus nunc. + +Suspendisse ultricies sem laoreet quam molestie ullamcorper. Sed auctor sodales metus, eget convallis eros ultrices quis. Duis rutrum mi a tortor iaculis posuere. Suspendisse potenti. Pellentesque dictum faucibus dui a vestibulum. Donec elit nisl, aliquet at dolor non, viverra dignissim felis. Donec mi elit, condimentum sit amet magna id, efficitur sodales arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque est elit, porttitor eget bibendum nec, auctor nec nulla. Pellentesque dignissim nisl vel orci commodo, ut ullamcorper justo viverra. + +Suspendisse pharetra gravida turpis sit amet efficitur. Nulla mattis tortor eget pharetra ultrices. Ut a turpis maximus, pharetra justo non, tempor quam. Nam et volutpat lectus. Vestibulum congue turpis augue, quis eleifend nulla euismod in. Vestibulum sed erat tempus, porttitor diam vel, elementum metus. Aenean facilisis molestie ante, sit amet ornare lacus mollis sed. Maecenas rutrum urna nec ex malesuada consequat. Nunc interdum pellentesque nisl sed viverra. Nam nec fermentum ipsum. Nulla facilisi. Maecenas congue dolor erat, a fermentum enim congue vitae. Integer metus libero, feugiat at eleifend eu, iaculis nec leo. Praesent eleifend convallis leo, eu posuere urna elementum eu. + +Aliquam dapibus dolor vel urna luctus venenatis. Suspendisse potenti. Donec vitae tellus nisi. Pellentesque vel neque dignissim, ornare tellus sed, sodales metus. Aliquam vitae maximus tortor. Nullam mattis, odio id porttitor euismod, est leo aliquet massa, bibendum pulvinar justo orci a magna. Morbi ut nisl congue, porttitor erat non, gravida turpis. In nulla risus, ullamcorper quis suscipit vel, tristique ut nulla. In malesuada odio augue, ac hendrerit nunc mattis a. Nam in quam ut elit ullamcorper mattis. Sed fringilla tempus felis, id pretium tortor varius non. Aenean quis sem quis risus mattis posuere vel quis nibh. + +Sed pulvinar commodo dui sit amet malesuada. Quisque porta tellus placerat congue efficitur. Cras id blandit enim. Praesent a urna felis. Aliquam ornare, nisl eget iaculis tempor, ligula mi vehicula dolor, ut eleifend massa massa vel est. Fusce venenatis laoreet lobortis. Proin tempus aliquet nunc ut porttitor. In est mauris, blandit eu mattis vitae, aliquam a orci. Cras vitae nulla nec ex cursus blandit. Aliquam fermentum, erat in aliquet dictum, metus urna fermentum quam, non varius magna lectus non urna. + +Donec faucibus nisl nunc. Integer rutrum dui enim, malesuada semper turpis pulvinar eget. Fusce consectetur ipsum tellus, sed maximus lorem auctor in. Morbi nec est in quam ultricies hendrerit. Sed aliquam sollicitudin elementum. Aenean aliquam ex sit amet dignissim venenatis. Duis malesuada leo nisi, id accumsan turpis pretium id. Sed ornare magna non pharetra pharetra. Ut auctor dolor neque, nec bibendum odio viverra in. Nulla convallis interdum condimentum. Proin vestibulum turpis in lorem ultrices, bibendum tristique ligula faucibus. In tincidunt auctor sem, vitae fringilla neque pulvinar eu. Etiam commodo pellentesque enim. Phasellus feugiat non sapien eget sollicitudin. Etiam consequat efficitur lacus vel maximus. Suspendisse vitae elementum diam. + +Mauris urna velit, efficitur at viverra at, interdum a velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean bibendum sagittis massa in interdum. Mauris facilisis dui eget ipsum euismod ultrices. Donec sit amet lorem iaculis, condimentum velit quis, lacinia justo. Integer faucibus metus sed eros semper, in congue tortor venenatis. Proin tincidunt maximus enim, accumsan facilisis tellus gravida eu. Phasellus interdum aliquam ante, sit amet rhoncus nisl ornare at. Suspendisse libero eros, cursus quis fermentum nec, tempor eget lectus. Aenean ullamcorper augue lacus, non iaculis dui rutrum id. Aliquam erat volutpat. + +Morbi hendrerit fermentum sodales. Proin rutrum congue auctor. Mauris pellentesque elit non velit condimentum tincidunt a sit amet velit. Etiam accumsan ante id neque commodo vulputate. Aenean nec mattis neque. Aliquam tempor urna quis nisl convallis congue. Praesent vitae porttitor ante. Mauris mattis vestibulum ante, nec auctor augue. Praesent sem leo, accumsan eu fermentum egestas, iaculis ac nulla. + +Etiam vel nisi congue, varius neque id, volutpat quam. Fusce placerat arcu hendrerit orci condimentum vehicula. Integer sem ex, facilisis et lacinia a, condimentum at ante. Morbi condimentum diam tellus, non lobortis arcu pellentesque sit amet. Phasellus imperdiet augue eget sollicitudin mollis. Vivamus sollicitudin arcu aliquam lectus tristique, in consequat diam egestas. Suspendisse aliquet non velit id feugiat. Sed eu est fringilla, gravida nulla ac, dignissim tortor. Phasellus pellentesque nisl non venenatis sagittis. Etiam facilisis nulla quis lorem scelerisque vehicula id at ex. Duis in risus enim. In eu vulputate massa, vel dignissim odio. Praesent ut mi tellus. In hac habitasse platea dictumst. + +Nunc malesuada turpis in fermentum lobortis. Donec blandit eu orci non laoreet. Suspendisse posuere blandit tortor, in accumsan velit cursus in. Integer suscipit justo nulla, in viverra orci suscipit et. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum a pulvinar lacus. Vestibulum mattis nisi vel nunc convallis maximus. Fusce aliquam, erat in volutpat gravida, elit odio feugiat ante, nec varius enim leo eget nisl. Donec sit amet ligula lobortis, sollicitudin dolor quis, blandit augue. Duis at interdum sem. Nullam eleifend ligula urna, vitae sollicitudin purus cursus nec. + +In commodo risus eu justo hendrerit, ut posuere mi eleifend. Suspendisse sollicitudin odio sem. Vestibulum at dapibus dui, vel dictum nisi. In hac habitasse platea dictumst. Sed ac pharetra ex. Ut ultrices augue ut vulputate condimentum. Phasellus convallis arcu tortor, ac tincidunt justo cursus vitae. Mauris dignissim dapibus imperdiet. Nullam id quam eget mauris cursus molestie finibus eu enim. Fusce laoreet orci eu nunc fermentum tincidunt. Pellentesque vitae ex ac nunc porta mattis sed ut ligula. Phasellus erat dui, consequat et lacinia vel, blandit nec dui. Donec ipsum magna, rhoncus ac viverra vitae, feugiat vel ligula. + +Cras ut nisl sed elit dictum semper a at magna. Aliquam laoreet viverra velit vel lobortis. Nam tempor lorem sit amet purus tincidunt accumsan. Vestibulum et vestibulum ligula. Donec sit amet neque faucibus leo rutrum semper. Maecenas scelerisque, lacus et lobortis congue, purus quam euismod risus, et mattis orci nisl eget est. In hac habitasse platea dictumst. Pellentesque eros velit, sollicitudin quis ante vel, blandit maximus mi. Suspendisse at eros id quam vulputate ornare. Duis placerat tellus vel odio ultrices, ac feugiat enim placerat. Vestibulum ut massa mattis, commodo orci euismod, cursus lacus. Suspendisse potenti. Sed venenatis cursus neque, at lacinia erat ornare et. Sed rutrum, dui at porttitor hendrerit, lacus magna fringilla quam, id mollis elit leo ut ante. Praesent vel diam sed urna mollis laoreet eget ut risus. + +Mauris varius odio lectus, sit amet consequat nulla sollicitudin sed. Suspendisse commodo rhoncus enim vitae pellentesque. Aliquam vulputate sollicitudin aliquet. Mauris interdum interdum orci, non varius sem ornare ut. Sed vel nibh nunc. Duis tincidunt quam quis lectus egestas, eu ultricies ante posuere. Aenean in mauris eros. Suspendisse potenti. Vivamus sit amet augue at velit accumsan fermentum. Phasellus vel dui sit amet felis convallis sodales. + +Nunc mattis vitae sapien ut dignissim. Nam fermentum sit amet massa eu accumsan. In ut ipsum sit amet ante pellentesque accumsan. Nulla egestas eros eget lacus rhoncus pharetra. Nam pellentesque laoreet ex in lobortis. Vivamus congue tincidunt molestie. Integer vel turpis augue. Cras vel molestie quam. Etiam vel mauris ut tellus finibus consequat. + +Curabitur velit erat, vestibulum blandit massa a, aliquam elementum tellus. Pellentesque id nisl tempor lorem condimentum dapibus. Quisque ut lorem at orci elementum dapibus quis ut enim. Praesent venenatis congue arcu eu sagittis. Nunc nunc massa, posuere ac gravida id, scelerisque nec velit. Suspendisse non lacus a orci dapibus congue. Sed leo velit, facilisis sed egestas ut, vehicula at turpis. Praesent a finibus felis. Quisque est mi, pellentesque sit amet varius eu, gravida id est. + +Donec auctor gravida urna ac suscipit. Etiam placerat ipsum vitae ante congue, at fringilla lectus ullamcorper. Donec viverra lacus ut erat posuere, dignissim porta purus tempor. Duis eget enim quis diam vehicula pulvinar. Donec tempus eros velit, sit amet pharetra ligula placerat in. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet pretium tellus, non dictum ligula. Nullam a ex purus. Vestibulum id ex rhoncus, pretium eros vel, consequat tellus. Vivamus lacinia dolor nec ipsum aliquam, euismod fermentum sem efficitur. Nulla facilisi. Pellentesque pharetra lacinia augue, et eleifend lorem aliquet eu. Ut ut porta ante. Duis ut auctor nisi, id porttitor erat. Proin sollicitudin sem quis justo sodales aliquam. Nulla pharetra gravida arcu vel tempus. + +Maecenas nec imperdiet nulla, vitae fringilla diam. Mauris maximus aliquet tellus at congue. Aliquam in dictum elit. Sed pretium mattis lectus, non pellentesque enim dignissim vel. Sed quis elementum diam, a porttitor enim. Cras cursus eros nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ornare sapien vel diam vestibulum, at commodo metus lobortis. Sed euismod iaculis scelerisque. Pellentesque venenatis malesuada dolor, at tempus eros venenatis sit amet. Fusce a massa non libero blandit suscipit. + +Nulla facilisi. Sed nec leo nisl. Proin condimentum nunc at risus semper, in laoreet dui congue. Integer in dolor vitae ex maximus porttitor. In efficitur vulputate metus, ac egestas diam pharetra ac. Sed tristique tempus ligula dignissim convallis. Ut tincidunt laoreet fringilla. Praesent nunc est, lacinia tristique odio in, varius rhoncus ipsum. Vivamus bibendum justo at metus ullamcorper imperdiet. Quisque elit nibh, rhoncus a placerat eget, vehicula quis ante. + +Morbi fermentum turpis enim, eu interdum dui interdum sit amet. Sed vulputate lacus nec ligula gravida euismod. Duis in nisl fringilla, sollicitudin diam ac, laoreet tortor. Sed eget porttitor augue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed eu enim convallis augue vulputate convallis. Praesent laoreet, leo at ornare luctus, nisi felis semper sapien, sit amet sodales augue mauris eu lorem. Fusce ornare volutpat malesuada. Curabitur vehicula, eros id fringilla placerat, tellus elit venenatis lorem, ac imperdiet purus ex blandit felis. Nunc suscipit elementum risus ac dictum. Aliquam bibendum dignissim ipsum, sit amet posuere sem viverra ut. Vestibulum egestas in ex ac volutpat. + +Donec ut faucibus velit, nec suscipit metus. Nullam dui ligula, commodo eget est venenatis, ullamcorper porttitor lectus. Suspendisse dictum, metus vitae commodo ultricies, enim quam pretium enim, a efficitur tortor purus sed ipsum. Nam sit amet lacus vel elit consectetur ultrices. Vestibulum ac nibh in metus porttitor vestibulum. In blandit et est non efficitur. Aenean vestibulum tortor sit amet mattis semper. Praesent sit amet turpis ac neque malesuada tincidunt nec nec urna. Mauris posuere elit nisl, ac ullamcorper nisi pulvinar in. Nulla ac elit in neque ullamcorper facilisis sed et dui. + +Quisque molestie euismod dui, non scelerisque arcu dictum a. Donec eu nulla nisl. Aliquam neque orci, ultrices in nunc vitae, sollicitudin eleifend augue. Etiam at mattis velit, a efficitur dui. Nam bibendum enim non elit tristique aliquet. Vestibulum eget nibh lacus. Pellentesque id sapien dui. Nullam urna leo, faucibus et efficitur vel, ullamcorper iaculis mi. Donec sit amet bibendum justo, id dapibus sem. Pellentesque dignissim varius tellus nec egestas. Praesent eu orci vel ipsum pharetra maximus. Cras nisl ligula, sollicitudin in ligula vel, posuere sagittis eros. Phasellus porta tristique mauris vel eleifend. Mauris sit amet volutpat ipsum, sed vestibulum orci. + +Ut rutrum augue quis rhoncus tincidunt. Aenean sollicitudin lacus at erat varius ullamcorper. Integer vitae orci vel nisi vulputate cursus eget nec ex. Mauris elementum, augue ac accumsan convallis, libero leo porta ante, sit amet elementum felis ligula non enim. Ut venenatis semper posuere. Vestibulum nec nisl nisi. Ut a sapien ac orci finibus dictum sed at ex. Phasellus ac lorem nisl. Quisque vitae tempor lacus. Vestibulum in mauris diam. Proin mattis ligula vitae ipsum bibendum, at dapibus nunc placerat. Nam iaculis justo in accumsan tristique. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris pretium velit et tristique pellentesque. Nunc in sapien a purus congue rutrum. Nam placerat risus in ante rutrum rutrum. In in ligula magna. Duis orci ante, vehicula elementum consectetur vitae, lobortis vitae arcu. Ut feugiat tempus metus quis sollicitudin. Etiam cursus venenatis augue at fermentum. + +Vestibulum id vehicula massa. Proin ut ligula et sapien placerat tristique non nec nibh. Etiam non lacus placerat, molestie ex eu, venenatis elit. Sed posuere, ante et ullamcorper luctus, elit lectus blandit tortor, nec consequat massa elit a tortor. Fusce urna felis, porttitor at ultrices vel, eleifend porta ipsum. Pellentesque nisl nibh, molestie sit amet sem eu, dapibus pharetra nulla. Phasellus viverra augue eu augue volutpat dignissim. Integer bibendum faucibus varius. Curabitur non turpis purus. + +Sed pretium eros nisl, sed ullamcorper magna faucibus quis. Etiam ornare euismod tellus, vitae accumsan eros bibendum ut. Morbi a ex sed risus faucibus rutrum et ut urna. Nullam non tortor commodo, facilisis massa non, tristique metus. Curabitur placerat, arcu in egestas ullamcorper, nisl nisl luctus felis, ac dapibus erat velit sed erat. Proin sagittis felis vitae sem posuere semper. Vivamus fermentum eu nisi ac facilisis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque pellentesque lacinia ex tempus lacinia. Aliquam erat volutpat. Nam aliquet mattis risus non pretium. Suspendisse potenti. + +Suspendisse et sapien ornare, elementum erat vel, ultrices nisi. Curabitur interdum dignissim tincidunt. Cras eget est a velit consectetur finibus et sed nisl. Curabitur vestibulum semper posuere. Aliquam porta diam commodo nulla tempus imperdiet. Sed id dictum neque. Ut sit amet risus aliquet, dignissim velit sed, tristique ipsum. Nam a sapien id magna commodo tincidunt quis ac tellus. Nunc nec pellentesque orci. In justo purus, vulputate quis urna a, fringilla blandit sem. Cras porttitor quam vitae metus vehicula faucibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut eleifend orci lectus, vitae semper eros hendrerit id. Fusce nec tellus condimentum, vehicula elit quis, gravida erat. Maecenas volutpat interdum velit id vestibulum. + +Pellentesque id metus metus. Nullam ac metus non nisi facilisis aliquet quis a sem. Morbi cursus consectetur aliquam. Morbi id sapien nibh. Etiam tempus tempor tempor. Nam scelerisque condimentum purus. Proin nec cursus eros, in condimentum dolor. Nam in sagittis urna. Ut eget ornare erat. Vivamus nec elit ut sapien tristique aliquet a nec urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas varius pellentesque diam. + +Ut eget libero iaculis urna porta iaculis vel ac eros. Sed sed mi et libero porta consequat a in libero. Sed in imperdiet ipsum, sit amet ultricies dui. Curabitur sit amet consectetur eros. Praesent nunc tellus, feugiat ac laoreet consectetur, accumsan nec magna. Praesent at elementum orci. Donec dapibus venenatis libero, id tincidunt libero euismod ac. + +Aenean sit amet ex placerat, molestie sapien a, volutpat turpis. Vivamus elementum lacinia nisi a convallis. Donec a sagittis turpis. Curabitur pulvinar laoreet dolor id consequat. Aliquam aliquam feugiat magna, vitae sagittis lorem mattis ut. Donec sed auctor nulla. Ut convallis, neque vitae faucibus efficitur, nisi justo pharetra odio, vitae tristique purus lorem et nisi. Quisque eleifend aliquam arcu nec maximus. Nunc mauris elit, finibus nec gravida non, maximus nec nibh. Sed eu ipsum in arcu gravida maximus. Maecenas massa dolor, ullamcorper eget odio eu, congue ornare leo. Suspendisse venenatis ultricies imperdiet. Nam finibus orci vitae sagittis finibus. Pellentesque sit amet dapibus diam. Nam eu nunc elit. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam euismod turpis nec urna lobortis, ut malesuada ex suscipit. Fusce sed viverra risus. Pellentesque tristique nulla pulvinar tincidunt accumsan. Nullam vitae nibh imperdiet erat rutrum pellentesque et sit amet quam. Aenean laoreet ultricies hendrerit. Duis elementum tellus a feugiat vulputate. Aliquam aliquam enim placerat dolor viverra, non suscipit lorem ultrices. + +Proin interdum vitae lorem quis viverra. Praesent nec tempor dolor, vitae sagittis turpis. Etiam sit amet imperdiet sapien, ac laoreet arcu. Proin aliquam, risus nec maximus dapibus, dui diam elementum dolor, vitae tempor augue lorem vel justo. Aenean ac egestas dolor. Ut aliquet, felis at fringilla venenatis, leo nisl ullamcorper ex, vitae pellentesque turpis orci quis lectus. Nullam vitae suscipit metus. Integer congue, ante in lacinia rhoncus, erat lorem interdum elit, egestas suscipit lectus nunc non orci. Nunc vel viverra quam. Mauris sit amet sodales tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis sollicitudin libero. In nec venenatis orci. + +Integer non quam fringilla, tristique nulla id, gravida arcu. Aenean scelerisque lacinia magna. Praesent nunc sem, lobortis non convallis rhoncus, rutrum vitae ante. Sed et orci ut erat viverra bibendum a et lectus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce sit amet eros eget dolor pharetra vestibulum. Nullam vestibulum, massa dictum finibus egestas, orci nulla pulvinar sem, nec tempor libero lacus eu ante. Nam lacus erat, gravida eu placerat sit amet, facilisis sed urna. Suspendisse efficitur felis non ornare dictum. Nam sit amet nulla enim. Nulla eget scelerisque est. Suspendisse lacinia velit arcu, id lobortis felis facilisis in. + +Suspendisse potenti. Nulla porttitor metus ut nunc dictum tristique. Cras sit amet tortor eget ligula tristique efficitur. Ut at nisi id purus imperdiet laoreet. Sed sit amet malesuada urna. In nunc dolor, luctus ac condimentum ut, dapibus vel metus. Suspendisse pretium urna eget libero convallis vestibulum. Integer ut mauris hendrerit ex posuere euismod sed sed odio. Nulla egestas libero sit amet magna venenatis faucibus. Pellentesque semper vestibulum elit, et pretium felis scelerisque non. Suspendisse aliquet leo arcu, sed dapibus ex semper non. Donec lacinia dictum dignissim. Maecenas ipsum nunc, aliquet eget consectetur sit amet, aliquet vitae odio. + +Proin consectetur blandit feugiat. Nulla ac dictum quam. Vivamus suscipit scelerisque ipsum, vitae consequat neque sagittis id. Donec eu augue hendrerit, varius ipsum at, cursus massa. Morbi id augue id ipsum porttitor mollis. Phasellus nec libero eu arcu finibus dapibus. Nullam nec pharetra ante. Aenean sit amet urna eget justo tempus pharetra ut nec mi. Pellentesque viverra, ligula nec elementum ornare, ante elit eleifend enim, nec dignissim ligula elit in nibh. Vestibulum facilisis, felis eu condimentum dapibus, ante risus tincidunt urna, ut posuere dui augue ut ante. + +Nam arcu lacus, accumsan ac dolor in, egestas euismod est. Sed consectetur mauris et enim tincidunt semper. Donec sit amet pellentesque diam. Fusce viverra arcu a placerat fermentum. Nullam euismod dui eget egestas ultrices. Duis euismod viverra tortor eget eleifend. Pellentesque vitae neque dapibus, bibendum mi eu, auctor velit. + +Pellentesque congue consectetur turpis, eget auctor dui suscipit vel. Aliquam a sollicitudin turpis. Praesent nec blandit tortor. Suspendisse non nunc at urna tincidunt elementum. Integer eu elit nec urna maximus blandit quis at tortor. Nulla laoreet elit a purus tempus, et tincidunt lorem sagittis. Aenean semper erat eu neque hendrerit ornare. Cras posuere lorem nec orci vulputate finibus. Fusce tempor ex ac lacus gravida venenatis. + +Phasellus laoreet, libero vel fermentum euismod, sem diam accumsan quam, vitae gravida arcu est at sem. In bibendum, felis at viverra congue, felis nunc fringilla libero, ut scelerisque erat massa ut neque. Suspendisse potenti. Cras faucibus dolor vel tortor blandit, quis imperdiet magna semper. Nullam ut urna dapibus, vulputate est a, hendrerit purus. Vivamus vestibulum nisi a tempus placerat. Morbi vitae ultricies lacus. + +Nunc vel efficitur urna. Fusce bibendum suscipit mauris, quis imperdiet nisl bibendum non. Nam sed nisi imperdiet, pellentesque sem sed, pellentesque diam. Praesent luctus feugiat odio, eget fermentum lectus ullamcorper non. Phasellus pulvinar lectus sed ligula semper lobortis. Pellentesque fermentum ultricies fermentum. Quisque id turpis vel orci rutrum rhoncus id vel metus. Vivamus ex leo, accumsan eu luctus sit amet, tincidunt vitae erat. Sed eu mollis odio, ut ultricies lacus. Duis enim odio, pellentesque non velit vel, aliquam blandit erat. Mauris feugiat felis fermentum purus blandit, id rhoncus lorem tempus. Integer cursus, nunc vitae laoreet commodo, nunc lacus venenatis orci, quis eleifend risus est nec quam. Nulla tincidunt nunc ac purus tincidunt, eu molestie ipsum sollicitudin. + +Vestibulum ac varius velit, non suscipit nulla. Vestibulum a sagittis tortor. Suspendisse vestibulum felis quam, eget blandit nulla dictum vel. Praesent eu tellus ut lacus facilisis ultrices. Etiam fringilla nulla nec leo viverra faucibus. Sed nec sollicitudin nisl. Aenean libero massa, ornare sed elit ut, interdum fermentum arcu. + +Aliquam maximus diam et elit dapibus, eget condimentum sapien finibus. Etiam gravida nunc dapibus facilisis feugiat. Sed quis massa ligula. Nunc eleifend dolor lacus, at dapibus nisi vulputate eget. Etiam vel euismod urna, quis molestie nibh. Vestibulum bibendum erat non dictum sollicitudin. Suspendisse sed placerat lacus. Cras sollicitudin quam justo, a condimentum urna feugiat a. Quisque mauris libero, ultricies at ligula a, facilisis euismod ante. + +Morbi hendrerit maximus sapien ut auctor. Nullam nisi erat, aliquam ac metus eu, fermentum laoreet augue. Nam ultricies mi at vulputate laoreet. In ipsum est, blandit sit amet lorem id, egestas aliquam odio. Praesent venenatis lobortis mi. Aenean eu lacus consequat, mattis enim et, pellentesque leo. Duis convallis facilisis placerat. Donec porta, enim eget placerat congue, nisi augue faucibus odio, et fringilla purus elit vitae felis. + +Nulla et tellus a libero pellentesque eleifend vitae mattis nisi. Nunc placerat neque et sapien lobortis, aliquet pharetra nunc vulputate. Suspendisse potenti. Etiam ullamcorper lacinia varius. Suspendisse sit amet malesuada magna. Nam molestie mi nec diam tempus laoreet. Suspendisse urna tellus, scelerisque vitae purus et, dapibus fermentum felis. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec scelerisque eget neque sed hendrerit. Donec at ligula augue. Donec vulputate massa nunc, a feugiat nunc elementum ut. Donec pulvinar libero sit amet ante faucibus sagittis. Mauris quis diam ultrices, tristique sapien nec, tempus urna. Morbi hendrerit odio sit amet leo lacinia, non egestas diam condimentum. Suspendisse efficitur sapien eget elit euismod tristique. Duis posuere vestibulum risus id sodales. Ut eget mi in urna rutrum molestie non nec dolor. Curabitur sollicitudin pharetra lacus, in sodales tortor tempor vitae. Nullam rhoncus arcu vel euismod varius. + +Aenean malesuada, purus quis volutpat sollicitudin, lectus risus lacinia mi, a facilisis ante lacus a dolor. Aenean vel aliquam tortor. Vivamus ornare, tellus at malesuada suscipit, quam dolor egestas erat, id convallis enim augue a enim. Aenean ultricies sodales placerat. Vivamus vel erat ac mi vulputate commodo nec eget urna. Suspendisse potenti. Mauris quis dapibus est, id tristique libero. + +Suspendisse ex diam, imperdiet quis dui id, interdum sodales elit. Maecenas at nunc ligula. Etiam imperdiet convallis magna sit amet tristique. Proin hendrerit laoreet magna, eget malesuada elit rhoncus et. Curabitur eget odio imperdiet, molestie sapien pretium, efficitur turpis. Aliquam sed congue arcu. Pellentesque vitae mauris id neque pulvinar tempor. Donec lobortis tellus id gravida dictum. Nulla et varius ex. Vestibulum pharetra eu quam dapibus finibus. Nullam accumsan sagittis turpis. Mauris fermentum orci et tortor tempus, ac tristique odio sollicitudin. Duis nec elit et magna imperdiet molestie eget vel ante. + +Suspendisse tempus augue nec massa molestie aliquam. Pellentesque vestibulum, erat sit amet fringilla fermentum, sapien lorem tempor felis, at efficitur augue erat quis nisi. Proin nec felis sagittis, sollicitudin turpis eu, fringilla leo. Vestibulum at augue nec dui vestibulum aliquam a at metus. Duis ullamcorper eleifend bibendum. Maecenas viverra mauris lacus, et consequat nisl pharetra eu. Praesent rutrum diam eu mi aliquet, non pellentesque est suscipit. Vestibulum massa leo, blandit eget mi at, cursus faucibus nunc. Praesent nec bibendum libero, vitae cursus libero. Sed consequat vitae eros nec maximus. + +Pellentesque et tortor eget lectus feugiat venenatis. Integer ornare nisl quam, nec imperdiet justo finibus at. Morbi malesuada, ante sit amet rutrum tempor, risus lorem tristique urna, egestas varius purus ex ut sem. Etiam sit amet feugiat sapien. Etiam quis risus commodo, eleifend velit quis, tincidunt enim. Mauris fermentum lacinia velit quis efficitur. Nunc sit amet lacus sit amet urna viverra feugiat. Nullam sagittis rhoncus suscipit. Aliquam eu sem ligula. Sed sodales risus et tortor lacinia tristique. Nulla massa augue, malesuada sit amet euismod ac, euismod placerat nulla. Sed lobortis ex nec lacus facilisis, nec semper mauris ultrices. Quisque ornare non felis vitae pellentesque. Donec mattis ut magna sed imperdiet. Integer viverra tempus feugiat. + +Donec laoreet aliquam imperdiet. Fusce justo neque, dictum vitae fringilla vitae, euismod sed augue. Fusce sodales, lectus a congue bibendum, ante eros pharetra tortor, eu sollicitudin libero ipsum sit amet felis. Curabitur sed condimentum quam, in iaculis ante. Ut feugiat dictum odio, vitae hendrerit lacus volutpat sed. Sed scelerisque et metus nec gravida. Mauris mattis porttitor magna, ut maximus justo sagittis vitae. Donec at metus ut leo gravida vehicula in sed diam. Vestibulum dignissim suscipit sagittis. Nam varius et arcu sit amet lacinia. Sed eget elit rhoncus, elementum dolor a, vehicula orci. Nam vitae tellus sapien. Phasellus massa lorem, commodo quis placerat in, semper faucibus tortor. + +Vivamus id dolor mi. Curabitur sagittis posuere quam in mollis. Phasellus finibus ipsum vel elit tincidunt, a bibendum ligula vulputate. Suspendisse quis purus nisl. Integer mi sem, posuere quis nisi a, consequat consequat magna. Vestibulum bibendum mauris in risus auctor fringilla vel at lacus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus venenatis mauris at mattis eleifend. Donec tempus ipsum ac nisl convallis ultrices. + +Nullam eu nibh ut erat vehicula accumsan. Vivamus vitae faucibus libero. In in luctus odio. Nulla nec enim pharetra, fermentum erat in, tempor turpis. Sed ac magna suscipit, dignissim elit id, faucibus libero. Maecenas placerat in dolor et faucibus. Sed maximus purus dolor, non egestas massa rutrum non. Nunc ut rhoncus lacus. Sed sit amet dolor eu sapien sagittis molestie. Aenean ipsum erat, rutrum a diam et, varius porttitor ligula. Phasellus fermentum fermentum felis. + +Phasellus odio massa, lacinia ac hendrerit vestibulum, placerat sit amet erat. Praesent eget justo scelerisque, congue est in, pharetra mi. Etiam blandit turpis dui, a faucibus ipsum viverra vitae. Praesent sed scelerisque arcu. Cras nec venenatis ipsum. In et tempus metus. Pellentesque cursus quis nibh quis porttitor. Duis leo lacus, pretium eget rutrum eu, tincidunt lobortis tellus. Cras in erat tristique arcu faucibus luctus sit amet tempus erat. Nullam placerat, est a interdum iaculis, purus justo convallis arcu, at mattis erat sem tempor velit. Curabitur sapien sapien, tempor eget sodales vitae, blandit ac libero. Proin eget sem et arcu malesuada convallis non a est. + +Ut id sagittis urna. Morbi est mauris, molestie quis magna ut, convallis porta justo. Etiam in vulputate sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam consectetur enim nisl, sit amet pulvinar turpis elementum at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum convallis cursus libero vel feugiat. + +Curabitur vel scelerisque metus. Etiam aliquet faucibus quam, vel aliquet est mattis quis. Pellentesque pulvinar sodales augue, a dignissim sem tristique vitae. Pellentesque dolor quam, pharetra vitae rhoncus ut, blandit at elit. Sed dignissim diam turpis, ac condimentum justo iaculis vel. Donec facilisis eros sit amet est dapibus, vel pellentesque eros consequat. Integer euismod mollis metus, vel rutrum risus imperdiet ac. Fusce semper dolor sit amet mi congue accumsan. Maecenas sagittis magna justo, vel varius ante scelerisque id. Fusce at urna sapien. Aliquam dui sapien, facilisis eu magna laoreet, feugiat tincidunt elit. Fusce iaculis sit amet erat id dignissim. Suspendisse sollicitudin laoreet consequat. Pellentesque eu mollis quam, vel dapibus libero. Duis mattis tortor vitae orci vehicula, et viverra ipsum lobortis. Quisque iaculis sapien est, id sagittis tortor rutrum at. + +Duis ut condimentum risus, et venenatis nulla. Praesent urna ex, faucibus eu mollis nec, lobortis vitae neque. Vivamus a malesuada tellus. Quisque bibendum dolor nec massa porta, nec malesuada magna auctor. Phasellus non auctor turpis, et bibendum risus. Ut pellentesque justo non neque convallis, ut imperdiet diam sagittis. Nunc arcu nisi, rutrum sagittis neque elementum, finibus consequat felis. Proin euismod elit eu urna tristique ultrices. + +Curabitur id hendrerit ipsum. Aliquam tortor nisi, dignissim vel sodales a, ultricies a ipsum. Etiam commodo arcu sed volutpat varius. Proin vehicula lacinia tempor. Vestibulum feugiat nibh eu ante tempor efficitur. Etiam eleifend a orci eu euismod. Cras a tortor risus. Cras nec urna non urna malesuada maximus vel et ipsum. In ullamcorper porttitor dolor, at fringilla tortor tristique ac. Nulla gravida tortor nec dolor convallis, accumsan sollicitudin dui luctus. Vestibulum euismod vestibulum semper. Nam semper lacus dolor, vitae rhoncus nisi blandit nec. Phasellus turpis dolor, posuere sed sodales sit amet, interdum ut arcu. + +Sed blandit nulla et sem vulputate, et semper lacus posuere. Proin velit lorem, sagittis tincidunt aliquet vitae, fermentum sed orci. Ut interdum ultrices dolor eu tempor. Quisque pretium vulputate dui at blandit. Aenean pretium urna sed purus dapibus aliquam. Mauris tristique augue pretium magna scelerisque, vel cursus sapien porttitor. Nullam neque justo, aliquam non neque id, lobortis facilisis nibh. Duis molestie dui eu augue tristique porttitor. Sed posuere justo massa, efficitur vulputate erat posuere non. Cras quis condimentum tellus. + +Etiam eleifend ipsum ut justo viverra porttitor. Nam bibendum enim nec ligula vestibulum, vel fringilla velit posuere. Praesent leo enim, varius sit amet nulla ut, sodales sollicitudin nunc. Phasellus hendrerit varius ex quis tempus. Suspendisse maximus laoreet enim, ut bibendum tortor. Ut non magna vehicula augue condimentum scelerisque. Aenean efficitur elit vel rhoncus euismod. Vestibulum sit amet sollicitudin dui. Quisque ac diam nibh. Nullam quam enim, volutpat eu turpis et, posuere consectetur neque. + +Ut tincidunt semper dictum. Etiam varius enim sit amet metus consequat malesuada in at ligula. Cras nisl dui, tincidunt a urna et, porttitor rhoncus velit. Mauris interdum imperdiet lectus ac mollis. Aliquam sollicitudin ornare mi, a varius nulla faucibus aliquet. Nunc fermentum tempor ullamcorper. Pellentesque laoreet libero condimentum pellentesque pharetra. Nullam sed eros vel erat vestibulum dictum. Nulla nec interdum dui, quis finibus nunc. + +Sed ac turpis in mi ultricies finibus sit amet et diam. Pellentesque sagittis non ligula et convallis. Suspendisse interdum est aliquet erat rhoncus semper. Donec pretium turpis ante, vel posuere mauris facilisis vel. Vivamus nibh ligula, pharetra sit amet hendrerit nec, vulputate ut sapien. Nulla ullamcorper condimentum metus vitae imperdiet. Ut eget ex purus. Nulla dapibus malesuada pharetra. Praesent sit amet ornare turpis. Nulla pulvinar felis eget arcu mollis vulputate. Vestibulum eget semper felis. Donec viverra justo id mi tempor, a mollis ipsum porttitor. Maecenas aliquet volutpat ante eget tempor. Duis ipsum nisi, scelerisque quis metus vitae, blandit faucibus nisl. Mauris rutrum nisi sed lorem dictum ultricies. + +Nulla dictum arcu augue, aliquet ornare ligula suscipit ut. Integer libero erat, bibendum quis arcu at, consequat luctus sem. Ut ut erat tincidunt, porta erat efficitur, bibendum neque. Maecenas eget convallis sapien. Nullam facilisis ex et lorem fringilla, ut convallis leo dictum. Duis porttitor, ex eu venenatis faucibus, erat augue dapibus leo, sit amet scelerisque neque dui sed arcu. Praesent at diam nec sem sodales condimentum. Vivamus vehicula urna tellus, a semper ipsum cursus id. Duis auctor enim erat, laoreet volutpat dui rhoncus maximus. Suspendisse pellentesque euismod lacus, at auctor purus. Suspendisse volutpat imperdiet diam, id laoreet est egestas at. + +Fusce lobortis ex vel condimentum convallis. Vivamus fermentum convallis dolor quis rutrum. Donec tempus ornare maximus. Mauris quam neque, dignissim rutrum maximus sed, volutpat sed lectus. Donec id augue gravida, pretium purus eu, sagittis tellus. Maecenas tincidunt gravida felis nec pulvinar. Fusce turpis urna, sodales non rhoncus nec, sodales ac ipsum. Suspendisse risus felis, imperdiet id malesuada fermentum, ultricies et erat. Suspendisse potenti. Pellentesque tellus ipsum, dapibus eget pellentesque eleifend, venenatis sed risus. Ut sed mollis nisl. Aliquam lobortis aliquam ipsum, et ornare magna bibendum ut. Proin quis justo vitae neque tincidunt maximus ultricies et purus. Nullam non semper turpis. Quisque non mauris odio. + +Donec commodo tempus egestas. Vestibulum at diam vel mi tincidunt finibus. Donec aliquam magna id eros tristique finibus. Cras ut enim sapien. Proin gravida risus a nisi dapibus, ac vulputate nunc laoreet. Donec at leo vel nulla iaculis feugiat sed et ante. Nulla a arcu at ligula sollicitudin semper sed eget est. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Ut dolor magna, luctus id elit ut, interdum viverra lacus. Aenean sed tempor metus. Aenean arcu dui, eleifend quis est sed, luctus lacinia nulla. Morbi id luctus libero. Maecenas sodales leo eu sapien maximus porta. Praesent gravida augue eu ligula pretium cursus. + +Duis aliquam luctus tortor, eu facilisis quam sodales sed. Phasellus feugiat venenatis lorem, in scelerisque est efficitur quis. Cras quis nisl nisl. Quisque magna felis, tempus nec pharetra et, tempor id ligula. Pellentesque sed dignissim velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris convallis iaculis posuere. Nullam orci velit, venenatis eget enim quis, molestie consequat dolor. Nulla egestas risus vestibulum sagittis blandit. Nullam dui lacus, tempus euismod sapien a, eleifend sodales justo. In id tortor neque. Vivamus faucibus ante ac odio vestibulum, vitae condimentum nibh consectetur. In rutrum hendrerit est, sit amet mollis ex aliquet tempor. + +Donec non tincidunt sem. Nullam dignissim felis nibh, et accumsan felis tristique sed. Maecenas id massa erat. Ut justo ligula, aliquet eu condimentum a, aliquet eget ex. Phasellus et neque eu nisl consectetur ullamcorper. Sed gravida efficitur nisi, vitae posuere nisi tincidunt sed. Duis vitae molestie metus, vitae finibus urna. Integer at lacus vitae turpis convallis feugiat a dignissim libero. In dolor nibh, aliquam eu malesuada in, tincidunt vitae nisi. Nullam at lectus risus. Integer vitae ligula interdum, congue nibh id, tincidunt dolor. + +Mauris risus quam, mollis eu consequat vitae, molestie sit amet risus. Vestibulum congue vulputate nibh sit amet ullamcorper. Nullam elit justo, hendrerit euismod elit nec, gravida tincidunt ipsum. Aenean tellus tortor, commodo vitae cursus vitae, finibus quis mi. Nam in tellus scelerisque, cursus magna a, elementum tellus. Praesent ut lacinia justo. Donec consectetur, mi nec faucibus viverra, nisl justo suscipit est, in consequat ex urna vitae orci. Fusce molestie feugiat ex sed dictum. Phasellus interdum eros dapibus sodales volutpat. Morbi elementum sapien ante, quis sagittis justo fermentum at. Maecenas vestibulum massa quis eleifend rhoncus. Praesent auctor ex at urna pellentesque facilisis. + +Duis tincidunt porta quam, in commodo orci dapibus interdum. Donec mattis erat ante, nec faucibus magna pharetra id. Aliquam dignissim ultricies auctor. Duis quis ex mi. Phasellus ipsum mi, volutpat quis augue nec, dapibus aliquet lectus. Aenean id eros mi. Fusce rhoncus iaculis magna, commodo commodo nibh pellentesque ut. Donec consectetur lacinia erat ut luctus. Mauris efficitur erat vitae sapien fringilla sollicitudin. Maecenas a egestas ipsum. Aenean placerat congue augue, ut condimentum lorem accumsan in. Morbi ac quam nunc. + +Nunc posuere faucibus semper. Integer at convallis ex. Duis a purus molestie, dignissim mi quis, consequat nulla. Proin tempus congue ligula, sit amet ultricies enim consequat ut. Nunc ac est at nisl euismod cursus. Pellentesque vulputate molestie ipsum. Praesent varius, lacus non lobortis blandit, nibh lacus scelerisque nisi, sit amet interdum ligula tellus ac purus. Vestibulum sed quam tempor, pharetra tellus ullamcorper, tincidunt est. Nulla tempus tortor nec erat tempus eleifend pellentesque eget purus. Duis gravida dui justo, ac commodo ipsum mollis et. Nullam vitae nibh enim. Ut sodales mi eu diam sagittis, quis luctus tortor euismod. + +Maecenas a augue ac augue varius rhoncus. Fusce nunc turpis, mattis eu iaculis a, dapibus nec purus. Pellentesque imperdiet sit amet purus a blandit. Morbi augue tellus, venenatis nec tortor in, consectetur tincidunt nulla. Aenean purus libero, volutpat quis neque vitae, mattis efficitur eros. Donec sodales venenatis augue, sed faucibus magna accumsan convallis. Pellentesque efficitur elementum imperdiet. Cras at condimentum leo. Curabitur feugiat ut nisi facilisis commodo. + +Sed commodo sapien quam, quis vulputate orci lobortis nec. Aenean luctus dictum ipsum, non consequat sapien molestie vel. Proin blandit libero tortor, vitae efficitur tortor hendrerit at. Sed eleifend eu velit eu tempor. Nunc auctor tincidunt mollis. Ut molestie, erat ut lobortis convallis, odio felis sollicitudin elit, vitae ultricies lorem neque et dui. Sed venenatis tempus ullamcorper. Integer eget elementum nulla. Maecenas a placerat ipsum. Etiam sagittis sagittis rhoncus. Aliquam faucibus magna id lacus pharetra, nec interdum lacus sodales. + +Mauris ipsum sem, venenatis non elit in, feugiat pretium lacus. Fusce eget tincidunt dolor. Nam pharetra lacus vitae diam bibendum, ac placerat tortor aliquet. Sed eget gravida orci. Ut in orci lorem. Morbi condimentum ligula dolor, in dictum mi vestibulum sed. Suspendisse eu velit finibus, tincidunt dolor malesuada, mattis est. Morbi iaculis sapien vitae metus facilisis fringilla. Donec sed volutpat neque. Mauris ipsum metus, venenatis cursus mattis quis, convallis ultricies purus. Aliquam quam magna, sagittis at mi quis, lacinia iaculis turpis. Praesent viverra, ex nec euismod lacinia, urna risus pretium odio, sit amet mattis metus eros non lectus. + +Nam sed vehicula est, ac aliquet mi. Aenean iaculis placerat orci, ut lobortis dui vestibulum convallis. Vestibulum eleifend ante sed lorem interdum lobortis ut vel tortor. Sed auctor id nisl sed bibendum. Fusce maximus vulputate mauris, tempus sollicitudin nunc efficitur vitae. Nulla pellentesque molestie leo, quis hendrerit enim. In vel dapibus nisi. Nam at dui quis eros porttitor volutpat in id magna. Maecenas quis quam a justo cursus aliquet viverra sit amet mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut hendrerit est at augue pretium condimentum at ut velit. Suspendisse non gravida metus. + +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Praesent lacinia commodo elit, sed fringilla ipsum imperdiet ut. Proin a dictum ante. Suspendisse potenti. Praesent ac nulla volutpat, ultricies diam vel, auctor risus. Nullam aliquam elit vel quam suscipit molestie vel ac dolor. Ut pharetra finibus elit, id vulputate ex dignissim in. Ut ac finibus urna, a luctus magna. + +Fusce suscipit convallis eros nec blandit. Cras quis lectus nibh. Donec pulvinar rhoncus pulvinar. Vivamus nulla nisi, vestibulum vel neque et, dictum gravida ex. Vestibulum in arcu vitae nibh auctor rhoncus ac in massa. Sed semper enim ac dui sollicitudin porttitor. Etiam ante erat, laoreet sed dolor eget, cursus commodo lorem. Curabitur maximus tincidunt nulla at molestie. Phasellus ultrices felis a cursus facilisis. Nullam a semper tortor. Nulla ac vulputate justo. Mauris maximus nisi quam, elementum eleifend nulla condimentum nec. Aenean congue tincidunt mi, id mollis orci blandit vitae. Integer placerat orci at nisl vestibulum lacinia. + +Sed egestas posuere egestas. Quisque pulvinar velit commodo felis accumsan, a consequat purus laoreet. Ut at arcu at augue sodales rhoncus. Ut non nisl dui. Sed sed nulla quis orci eleifend auctor ut et sem. Aliquam erat volutpat. Donec egestas tincidunt leo in interdum. Donec varius odio nulla, id porttitor erat venenatis ut. Nulla ac nisl ut enim placerat congue. Fusce consequat purus eget risus luctus finibus. Donec in blandit odio. Sed vestibulum nisl ut diam vestibulum volutpat. Donec magna quam, tempor eget velit quis, blandit tristique lacus. Pellentesque et orci dui. + +Integer tempor mollis purus ut volutpat. Pellentesque efficitur cursus neque in ullamcorper. Integer ac tincidunt lacus, at venenatis tellus. Curabitur convallis commodo enim a convallis. Aenean sagittis sodales nibh, sed eleifend diam ultricies at. Vestibulum erat mauris, lobortis ac tempor a, viverra non sem. Nulla non ipsum mollis, dignissim elit a, laoreet enim. + +In laoreet purus eget orci rhoncus viverra. Quisque vel metus quis nulla hendrerit viverra. In consectetur velit vitae purus vestibulum, in hendrerit odio sollicitudin. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ut est sed ligula tincidunt fermentum nec ac nulla. Nam in sagittis velit. Vivamus vel dapibus erat, ullamcorper sollicitudin metus. + +Quisque pulvinar nulla eget mi mattis, ut convallis nulla ullamcorper. Vivamus placerat mauris vel elementum aliquet. Sed ac accumsan eros. Vivamus vitae rhoncus urna. Fusce gravida cursus varius. In posuere finibus leo cursus molestie. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris vulputate mi ut nunc finibus convallis. Praesent luctus erat eu urna facilisis convallis. + +Nam efficitur tempus augue varius porttitor. Pellentesque scelerisque dolor scelerisque, dignissim massa eget, iaculis nibh. Praesent viverra molestie dui a pulvinar. Mauris malesuada mattis felis, vitae sagittis metus pellentesque viverra. Phasellus faucibus congue blandit. Pellentesque mattis, justo sed imperdiet rutrum, metus metus porta nisi, vel malesuada lorem massa at dolor. Nullam nisi eros, tristique quis mollis ac, hendrerit nec leo. Praesent viverra molestie vestibulum. Nullam eu aliquam risus, at dignissim diam. Quisque blandit fermentum erat, sed ullamcorper augue pellentesque in. Integer eleifend libero non lacus sagittis auctor. Aliquam volutpat interdum accumsan. + +Etiam in eros porta, bibendum lacus eget, feugiat orci. Integer massa dui, commodo ac luctus nec, ornare id velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis dignissim scelerisque ex. Aliquam tempus cursus nibh, sed scelerisque libero sagittis ut. Morbi finibus vestibulum nulla, nec aliquam urna venenatis vulputate. Pellentesque ultricies, lorem a dignissim bibendum, augue nisi eleifend libero, cursus ultrices nulla purus a nunc. Quisque dictum pulvinar turpis vitae finibus. Etiam eget ultrices diam. Sed commodo enim ante, fermentum rhoncus tortor tincidunt ac. Suspendisse fermentum aliquet erat non posuere. Aenean vel dapibus lorem. + +Aenean lorem libero, rutrum vel egestas vel, pellentesque ut ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum felis diam, pellentesque sit amet ultricies sed, vestibulum in leo. Morbi aliquet, quam ut fringilla sollicitudin, justo risus suscipit mi, in vulputate neque erat ut turpis. Duis auctor dui non urna sagittis dictum. Vestibulum nec felis vel sapien congue volutpat a a orci. Phasellus tincidunt libero ac lorem feugiat, ac elementum lectus eleifend. Praesent sit amet est id massa convallis congue. Integer ac nunc eget orci pharetra vehicula. Mauris et ultricies diam. Nunc et pellentesque lacus, eget bibendum sapien. Morbi condimentum mi ac metus euismod convallis. Proin consequat rhoncus varius. Maecenas feugiat elementum vulputate. + +Vestibulum tempor feugiat dapibus. Ut ornare in augue non euismod. Nulla faucibus gravida condimentum. Curabitur pharetra dui non lorem sagittis iaculis. Quisque vitae nibh magna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce quis pharetra sem, sed aliquet lectus. + +Fusce volutpat tempor tincidunt. Pellentesque ultricies imperdiet tincidunt. Integer porta dictum lorem, non luctus tellus bibendum sit amet. Quisque posuere felis et est dapibus, commodo rutrum leo molestie. Fusce sodales felis sed sem pharetra pretium. Praesent nec sollicitudin nisl. Donec volutpat convallis elementum. Duis id libero augue. Nulla facilisi. + +Nulla viverra, urna nec efficitur vulputate, metus odio lacinia purus, in sagittis elit erat vel massa. Fusce ligula leo, finibus non felis dignissim, dictum ullamcorper odio. Phasellus vel eros eu sem venenatis sagittis a nec nulla. Pellentesque sapien elit, lobortis quis dictum et, maximus vitae metus. Curabitur quis aliquam eros. Quisque cursus condimentum urna vel efficitur. Etiam aliquet lorem ut varius suscipit. In viverra molestie felis vitae vehicula. Curabitur cursus, nunc sodales faucibus ullamcorper, urna magna dapibus velit, accumsan blandit mi quam nec justo. Donec ac dui lorem. Sed eu sagittis nulla. Suspendisse ut ante lacus. Fusce non tristique felis. Nulla convallis consectetur arcu, semper egestas urna efficitur quis. Nulla ac turpis at leo pretium maximus. Morbi cursus diam ac purus imperdiet, non commodo tellus cursus. + +Mauris ut eros suscipit, suscipit nisi vel, porttitor sapien. Morbi in nulla sapien. Cras maximus pretium justo, vel eleifend urna vulputate sed. Aenean egestas nisl ex. Curabitur sed pharetra ligula. Nulla blandit lorem id mauris tincidunt, at elementum risus aliquet. Quisque id interdum dui. + +Aenean nec elit condimentum ex viverra hendrerit. Pellentesque blandit nibh elit, a ultrices orci vestibulum vitae. Donec ultricies malesuada justo ac luctus. Pellentesque laoreet nunc a sem varius, ac dapibus ligula volutpat. Aenean sem felis, aliquam nec ullamcorper quis, ullamcorper non ante. Pellentesque libero leo, sagittis nec tempus a, tincidunt eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec elementum massa vel diam suscipit suscipit. Suspendisse eget neque porta, cursus orci eget, imperdiet libero. + +Vivamus faucibus vulputate sapien, non pulvinar ex sodales vitae. Maecenas consequat est urna, id faucibus eros dictum at. Vestibulum tortor mi, porta vitae sagittis sed, convallis quis enim. Vestibulum gravida justo pulvinar sem ornare, efficitur dictum neque varius. Nullam egestas congue tellus vitae interdum. Nam lectus erat, porta vitae lorem non, mollis semper velit. Nulla vestibulum tincidunt elit. Phasellus sed vulputate leo. + +Sed efficitur quam ac iaculis volutpat. Praesent nec feugiat ex. Aenean at ligula iaculis, imperdiet lacus et, condimentum magna. Integer euismod leo id mauris condimentum, quis semper lacus blandit. Sed volutpat neque urna, sit amet ullamcorper est dignissim non. Vestibulum tristique sollicitudin risus, sed hendrerit massa finibus id. Vestibulum sit amet risus non eros vehicula malesuada. Nam facilisis lacus sed quam pulvinar, at fermentum lectus tincidunt. + +Vivamus laoreet sem nec odio blandit, ut ornare sem egestas. Suspendisse potenti. Nulla aliquam pretium volutpat. Maecenas a orci suscipit, aliquam eros a, maximus urna. Aliquam eu gravida justo, et hendrerit justo. Suspendisse a commodo lorem, quis viverra nisl. Nulla vel pellentesque nisl. Nulla ligula tellus, vehicula sit amet turpis in, sodales tincidunt tellus. Proin ut nibh sed ipsum porta dignissim vel sed mauris. Interdum et malesuada fames ac ante ipsum primis in faucibus. In hac habitasse platea dictumst. Vestibulum efficitur nulla rutrum, accumsan lacus at, dignissim tortor. Morbi egestas metus ut nibh tincidunt posuere at sed elit. Integer eget hendrerit nulla. + +Donec sagittis sagittis tempus. Proin iaculis neque vehicula commodo faucibus. Pellentesque erat ante, vehicula sed efficitur vitae, varius non sem. Mauris pellentesque, felis nec egestas scelerisque, nulla nunc fringilla arcu, feugiat fringilla quam neque in elit. Nullam ut ex odio. Mauris enim risus, consequat at suscipit at, pulvinar ut arcu. Fusce mollis sem sed tellus tempus pellentesque. In pharetra, libero sed tristique vestibulum, massa velit egestas risus, at mollis augue lorem sit amet turpis. Mauris risus dui, sagittis vitae congue ut, condimentum ut augue. Quisque non sollicitudin purus, sit amet consectetur sapien. + +Sed scelerisque ipsum sed augue varius, sit amet ultricies purus posuere. Etiam vehicula at nunc in posuere. Cras eget risus a sapien mollis facilisis. Morbi vel purus auctor, faucibus mauris non, malesuada leo. Donec venenatis consectetur libero in pretium. Ut efficitur molestie metus id scelerisque. Nunc dolor justo, pharetra vel sagittis vitae, placerat ut justo. Donec tempor fermentum est semper auctor. Nullam tincidunt risus non risus elementum varius. Suspendisse euismod augue metus, nec pellentesque enim sagittis ac. Morbi et lectus quis justo commodo varius commodo vel risus. In bibendum purus in erat ullamcorper, sit amet dignissim sapien scelerisque. Quisque tempus elementum rutrum. + +In viverra sollicitudin pretium. Sed finibus scelerisque sollicitudin. Nulla lobortis purus in lectus iaculis, a ornare ex accumsan. Morbi malesuada mollis placerat. In hac habitasse platea dictumst. Pellentesque sed dui quis odio scelerisque molestie eu nec libero. Proin viverra magna ligula, at luctus lacus posuere sed. Nullam non congue mi. Praesent non mattis neque, sit amet tempus diam. Mauris eget cursus lacus, id dictum mi. + +Sed semper velit in vehicula tristique. In lobortis est augue, et maximus tellus iaculis ut. Proin quis ex maximus erat iaculis imperdiet. Curabitur ultrices turpis ac nibh congue ornare. Fusce a aliquet felis, nec sodales tellus. Nunc mauris ante, feugiat et facilisis at, pharetra ultricies dui. Integer felis metus, porttitor ac ipsum vitae, placerat varius ex. Morbi in dui a nunc aliquam posuere. Nulla tempus tortor ac lorem consectetur sodales. Praesent sit amet placerat diam. Curabitur sodales mauris ante, et tincidunt lacus ultricies quis. Nulla eu finibus nisl, sit amet volutpat leo. Donec ligula enim, feugiat non aliquet nec, congue interdum lorem. Pellentesque a nisi blandit, semper massa sit amet, auctor eros. + +Aenean ligula libero, commodo a erat at, tristique facilisis quam. Sed congue fringilla lacus, vel iaculis quam suscipit ornare. Curabitur non pretium mi. Donec condimentum odio dui, id malesuada ipsum porta ac. Aliquam imperdiet cursus ullamcorper. Duis cursus tincidunt nibh sit amet cursus. Aliquam urna orci, sodales ac ipsum at, placerat lobortis neque. Sed sit amet convallis felis, vestibulum finibus arcu. Phasellus venenatis egestas felis, id aliquam turpis iaculis non. Proin a lacus ut felis malesuada sodales. Duis elementum, eros ac placerat bibendum, quam nisl varius mauris, vel venenatis neque augue et justo. Nunc varius sem velit, vel tempus mi aliquet id. Fusce purus massa, mollis id nulla non, dignissim cursus massa. Sed sollicitudin interdum felis, nec eleifend nisl feugiat eget. Nulla rutrum id odio ut consectetur. Maecenas fermentum turpis vitae libero ullamcorper suscipit. + +Vestibulum auctor lectus ligula, non sollicitudin odio maximus nec. Cras faucibus, felis sed suscipit tempor, risus lorem consectetur est, eget pulvinar nibh dui eu dolor. Vestibulum pellentesque, ex aliquam vulputate laoreet, libero lorem rhoncus risus, vitae congue velit justo vitae nisi. Nullam placerat venenatis tortor, sit amet lacinia augue placerat nec. Quisque vulputate lectus vel pulvinar condimentum. Nullam at libero iaculis, mattis purus vel, tincidunt diam. Suspendisse eu ullamcorper eros. Nulla id eros odio. Ut enim leo, ultricies quis mauris sed, interdum commodo felis. Etiam enim lacus, scelerisque a interdum eget, mollis vel tellus. Phasellus vel vulputate orci. Nulla ornare leo sed arcu rhoncus convallis. Duis libero arcu, pulvinar ut tellus vitae, aliquam euismod magna. Donec semper nisi eget nulla tempus, sed condimentum massa sollicitudin. + +Suspendisse bibendum ante vitae ullamcorper semper. Praesent dui est, elementum nec lacus in, pellentesque accumsan sapien. Cras quis urna at lacus posuere commodo eget nec arcu. Nunc varius ante quis ligula rhoncus, ut dignissim metus commodo. Integer volutpat gravida nunc eget faucibus. Quisque imperdiet, mauris vitae cursus malesuada, lacus mauris tempus sem, ut accumsan purus lectus quis nisi. Vestibulum aliquam dui sed nisl laoreet, id posuere quam imperdiet. Aliquam ultricies non enim vel tempor. Donec sed feugiat justo, vel rutrum enim. Phasellus lorem quam, dapibus a tincidunt vulputate, pellentesque non lorem. + +Nunc quam diam, condimentum sit amet enim semper, hendrerit laoreet magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam et pulvinar leo, ac fermentum lectus. Sed erat ante, mattis ac tempor vitae, euismod at tortor. Ut sagittis fringilla scelerisque. Ut pulvinar molestie leo eu faucibus. Sed quis efficitur purus. Pellentesque nibh nisi, porta id orci id, sagittis sodales tellus. Ut venenatis velit a lectus lacinia, at ultrices nisi commodo. Vivamus eros orci, ornare vel ullamcorper elementum, posuere id ligula. Aliquam non massa pellentesque, semper ipsum scelerisque, dapibus leo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla pretium leo diam, sit amet bibendum lacus tempus quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer quis pulvinar nibh. + +Mauris sed eros nisi. Cras malesuada, turpis vitae laoreet porta, sapien odio maximus nulla, ut efficitur mauris odio tincidunt elit. Nam ut urna eros. Sed at vestibulum risus. Nulla luctus pulvinar rhoncus. Pellentesque maximus ligula a est ullamcorper, sed tempus tortor ultrices. Curabitur ligula odio, mollis sit amet risus quis, tempor auctor magna. Suspendisse vel quam quis lorem commodo facilisis. Donec eu ipsum suscipit, molestie dui sed, fringilla dui. Proin placerat ex a lorem sodales convallis. Sed molestie quam mi. + +Fusce laoreet nec dolor id bibendum. Nunc condimentum vulputate massa lacinia fermentum. Donec maximus cursus eros sed porta. Nunc eu porta turpis. Nulla cursus et nisl eu elementum. Ut rutrum scelerisque aliquet. In tellus erat, rhoncus id tempus vitae, vestibulum non quam. + +Vivamus luctus elit a efficitur dapibus. Praesent magna mi, pellentesque sed accumsan dapibus, blandit at lectus. Praesent sodales erat diam. Pellentesque placerat lobortis enim, a dapibus ligula molestie ut. Phasellus dui augue, suscipit ac urna non, iaculis eleifend augue. Maecenas sed elit iaculis, pretium ligula lacinia, aliquam libero. Nulla sem mi, pellentesque viverra lorem vel, luctus vulputate augue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque fermentum nunc ex, at lobortis turpis congue vitae. Suspendisse iaculis elementum enim commodo facilisis. Vestibulum non neque tellus. Proin vitae sodales urna. Mauris interdum purus et neque tempus, vitae mattis libero finibus. Suspendisse iaculis condimentum nibh, eu mattis enim vehicula a. + +Fusce dictum urna non lectus ullamcorper vestibulum id in elit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc fermentum odio non ante sollicitudin posuere. Praesent vulputate quam nec magna euismod pellentesque. Etiam tempor nunc eget velit volutpat eleifend. Mauris sodales nunc ullamcorper, gravida purus eget, rutrum sem. Nam a sapien rhoncus, scelerisque lacus ac, condimentum ipsum. Pellentesque suscipit, ligula ut rhoncus ullamcorper, ligula augue rutrum lorem, eget aliquam risus tortor eget metus. Curabitur scelerisque bibendum purus vel vulputate. Morbi et odio at neque sodales suscipit. Nam at pretium nulla. In scelerisque vulputate suscipit. Nulla facilisi. Pellentesque eu sem eu ligula efficitur viverra. Pellentesque gravida consequat urna id bibendum. + +Nulla consectetur facilisis est nec aliquam. Proin hendrerit magna suscipit ante accumsan hendrerit. Nulla ut sem id neque tempor vehicula. Sed eget massa eget sem placerat tincidunt. Ut eleifend, justo sit amet egestas lacinia, ex velit pharetra nulla, nec aliquet elit neque ut turpis. Quisque in gravida velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed euismod malesuada convallis. Ut ultricies, magna et blandit fringilla, dolor dolor maximus sapien, in aliquet augue est vel odio. Nulla commodo elit massa, euismod tempor turpis aliquet eget. Morbi non odio et tortor egestas ultrices. Etiam semper, ipsum et dictum pharetra, eros purus bibendum lacus, vel laoreet lacus sem vehicula nibh. Nunc enim nulla, pulvinar sit amet lobortis sed, porttitor molestie risus. Morbi tincidunt diam mi, nec auctor sem rutrum at. + +Donec pellentesque odio odio, eu condimentum risus consectetur quis. Vivamus ullamcorper, mauris non semper rutrum, dui risus suscipit tellus, ut tempor velit risus vitae nibh. Duis tempor nibh at tristique rutrum. Cras congue nisl at sem sollicitudin efficitur. Aenean auctor purus vel libero fermentum elementum. Mauris convallis orci id interdum accumsan. Aliquam at metus risus. Sed accumsan, quam id dignissim congue, velit risus eleifend ligula, ut fringilla elit erat at neque. Aliquam tempor, quam ut ultrices volutpat, orci lectus vulputate turpis, eget pharetra purus nisi non lorem. Ut condimentum convallis justo, a volutpat eros. Sed tempus turpis leo, in convallis est fringilla sed. Vivamus eu laoreet tellus. Quisque ullamcorper ullamcorper leo. Nam fermentum egestas facilisis. Nulla egestas ligula feugiat urna molestie, faucibus convallis erat accumsan. Cras pellentesque ipsum lectus, vitae luctus dolor suscipit nec. + +Vivamus vel nibh sed mi tincidunt cursus quis facilisis tellus. Donec eget est eu velit pretium molestie. Maecenas lacinia risus turpis. Sed tristique id risus sit amet venenatis. Duis maximus, metus ac molestie convallis, quam ex dapibus sem, at rutrum metus nisi quis lectus. Suspendisse sodales in nisi at hendrerit. Maecenas suscipit lobortis vulputate. Nunc convallis sit amet elit eget porta. Mauris tincidunt massa in augue finibus iaculis. Vestibulum imperdiet, orci vel bibendum laoreet, ligula quam mollis lacus, a eleifend nisi tellus vitae ipsum. Cras non ante sollicitudin, auctor dolor id, condimentum tellus. Morbi malesuada leo nec lectus scelerisque, nec interdum lacus ornare. Integer ultrices ligula nunc, sed blandit urna aliquam eget. Proin consequat viverra ex non rhoncus. Phasellus id nibh at tortor sodales blandit. Donec dignissim ipsum vel ligula malesuada rhoncus. + +Nullam mauris sem, dapibus ac nisl at, hendrerit faucibus nisi. Mauris dictum fermentum pellentesque. Praesent in gravida odio. Vestibulum pharetra iaculis est quis tincidunt. Sed venenatis rhoncus lacus, non porta ante vulputate at. Duis fringilla ipsum at urna euismod molestie. Duis a porttitor ante. Mauris pharetra elit et metus auctor, a eleifend sapien porta. Vivamus ut tincidunt purus, lobortis euismod tellus. Nullam non nulla gravida, laoreet tellus ac, placerat urna. + +Sed justo sapien, scelerisque nec nisl vel, efficitur aliquet purus. Phasellus eget posuere nisl. Integer porta vel purus nec ornare. Pellentesque rutrum in nulla at hendrerit. Nullam elementum sodales volutpat. Duis tempus, purus sagittis auctor ullamcorper, dui erat hendrerit nulla, id finibus eros dui quis risus. Quisque posuere nunc quis augue placerat lacinia. Nunc quis lorem vulputate, tincidunt enim nec, rhoncus odio. Ut porttitor porta velit ut vulputate. Duis convallis sagittis magna nec gravida. Ut eu luctus sem, non placerat erat. Vivamus viverra ut ligula ac finibus. Cras ligula urna, tincidunt id risus eu, dapibus viverra lorem. Aliquam erat volutpat. Sed dolor nisi, faucibus non ligula faucibus, laoreet bibendum diam. Vivamus sagittis lacus ut condimentum tincidunt. + +Proin tristique mi ullamcorper dolor accumsan mollis. Nulla facilisi. Pellentesque iaculis mattis augue ac rutrum. Mauris in nulla at ligula fringilla pulvinar. Nam commodo ornare nibh ac viverra. Maecenas eget augue a risus mattis ullamcorper non at nibh. Aenean nec erat diam. Pellentesque augue nisl, venenatis mollis dolor non, bibendum malesuada leo. Pellentesque elementum purus ornare mauris iaculis porttitor. Sed mollis tempor dui eu elementum. Donec porttitor tellus non mattis gravida. Mauris venenatis suscipit convallis. Sed dolor sapien, pellentesque et tellus a, tempus cursus magna. Nunc lobortis leo magna, at maximus orci ultrices eget. Nullam vulputate dictum nisi, vel egestas orci. + +Nam nibh mi, ornare sit amet nibh vel, lobortis lacinia leo. Ut egestas mi vel nunc luctus vestibulum. Nullam id tempus felis, eget convallis erat. Aliquam venenatis ut tellus id fringilla. Aliquam erat volutpat. Sed vehicula, odio nec mollis dapibus, ligula sapien consequat risus, ut volutpat diam neque ut neque. Mauris venenatis libero vitae sem elementum, sit amet maximus massa varius. Maecenas malesuada mi id leo euismod, eu maximus sapien vehicula. Nulla facilisi. Duis nec venenatis ante, non pulvinar lacus. In sollicitudin sit amet velit suscipit egestas. Maecenas a lectus euismod, dictum nulla at, malesuada lectus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec lorem massa, laoreet non elit nec, sollicitudin blandit felis. + +Cras dignissim fermentum tellus ut fringilla. Maecenas maximus eros pretium sagittis venenatis. Maecenas id enim ac est semper efficitur ac hendrerit leo. Cras eu arcu tincidunt risus pellentesque aliquam. Pellentesque vel sodales libero, non rhoncus enim. Integer vulputate vulputate libero, eu consectetur eros euismod vitae. Fusce magna nibh, vehicula sed turpis eget, malesuada ultricies sem. Sed convallis sem nisl, rhoncus mollis mauris bibendum ut. Pellentesque vitae massa a justo dapibus laoreet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed mollis egestas nisl vel ultricies. Phasellus sit amet varius leo, sed vehicula sapien. Etiam in urna eget neque aliquet ultricies in nec quam. Aliquam at feugiat elit. + +Maecenas placerat nisi ultrices neque pulvinar, et ultrices ante tempus. Vestibulum laoreet quam lacus, eget commodo magna dictum consectetur. Aliquam erat volutpat. Aenean tincidunt ipsum sit amet justo aliquet tempus. Quisque blandit sit amet est facilisis dictum. Sed non consequat dui. Integer purus diam, gravida quis tortor lobortis, iaculis vehicula sem. Morbi pellentesque est elit, vitae interdum elit laoreet et. Vestibulum in blandit ante, eu blandit velit. Morbi sagittis eu est eu vulputate. Nullam ac mi feugiat nibh feugiat suscipit. Sed interdum tincidunt efficitur. Integer dictum sem erat, ut pharetra libero lacinia in. Mauris at hendrerit odio, a facilisis lacus. Donec blandit massa ac nisi ultrices faucibus. + +Aenean tempus id ligula at mollis. Cras id dui magna. Integer id neque tincidunt, rhoncus nunc et, laoreet nibh. Pellentesque consectetur odio ligula, et consectetur tortor scelerisque non. Cras quis ex pharetra mi ornare lobortis. Phasellus fringilla interdum felis, vel aliquam ligula. Mauris cursus varius turpis in iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent eget mi id nulla semper dapibus. + +Vivamus lacus augue, eleifend dignissim ipsum eu, interdum accumsan massa. Nam id quam vel ligula consectetur volutpat id eget eros. Aliquam sed felis velit. Duis egestas velit ac dolor blandit, ut elementum mi tincidunt. Integer varius nisl a sapien pellentesque, et pellentesque ante elementum. Fusce ac orci interdum, faucibus nisl ut, sagittis nisi. Vestibulum sed diam eu magna congue ornare. Integer dignissim ligula sit amet mauris pretium vulputate. Duis ac tincidunt magna. Sed vehicula, ante nec vulputate venenatis, mauris odio blandit dolor, vitae lacinia nibh lorem et metus. + +Sed non sapien vel lorem tempor semper ac ac tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas auctor ante sit amet suscipit vulputate. In ac cursus turpis. Donec eu sem bibendum, blandit est nec, blandit tortor. Pellentesque scelerisque justo vitae magna cursus, vitae egestas libero interdum. Pellentesque vitae ligula vel mauris vehicula convallis. Nunc ornare lectus sit amet lorem aliquet dignissim. Cras nec ligula pretium, semper nulla a, pulvinar lacus. Sed ac augue bibendum, posuere ipsum eu, venenatis quam. Sed elementum nunc quis odio pellentesque, a vestibulum ex congue. Cras id malesuada arcu. Mauris ut egestas nulla, sed tempus ligula. Donec ac elit ut nisi pulvinar elementum. Suspendisse id auctor lectus, vitae ultricies lacus. + +Vestibulum pulvinar ornare cursus. Curabitur accumsan sollicitudin mi in vestibulum. Vestibulum vulputate tincidunt luctus. Suspendisse potenti. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus a mattis neque, id malesuada odio. Aliquam id auctor magna. Vestibulum quis nibh lacinia, tincidunt felis sed, vestibulum ex. + +Donec placerat ipsum diam, vel imperdiet velit eleifend ac. Quisque dapibus erat non dui convallis eleifend. Praesent pellentesque felis id suscipit rutrum. Donec quis lacinia turpis. Nulla justo eros, fringilla vel fringilla in, euismod sollicitudin arcu. In ut lobortis arcu, at rhoncus elit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Pellentesque iaculis quam nec interdum eleifend. Fusce mauris mauris, imperdiet sollicitudin volutpat eu, sollicitudin blandit leo. Vestibulum hendrerit diam gravida erat imperdiet, blandit dignissim dui tempor. Phasellus viverra ante quis aliquam finibus. Duis sed condimentum augue, nec sollicitudin dui. Ut ullamcorper metus ac ligula accumsan, ac fringilla metus gravida. Morbi rhoncus est nunc, at lacinia ex fringilla sit amet. Integer lobortis ultricies nisi vitae accumsan. Nulla nec sagittis ante. Mauris bibendum nisi ut magna vulputate maximus. Praesent in elit eu metus dignissim cursus. Ut a tellus felis. Mauris eget ornare diam. + +Suspendisse potenti. Nam nec tortor ex. Duis eu elementum ligula. Pellentesque efficitur sodales orci, vitae sollicitudin odio tempor ac. Nam lacus ante, lobortis vitae lobortis eu, eleifend et velit. Nulla et lectus eu nibh tristique malesuada a sit amet felis. Proin molestie, lectus vitae sagittis malesuada, massa velit finibus est, id vestibulum dolor felis eget lectus. Phasellus varius pellentesque neque, a malesuada ex mattis eget. Nulla facilisi. Phasellus interdum placerat lobortis. Sed et odio tristique, viverra libero et, sagittis diam. Proin tristique lectus id bibendum maximus. Integer ornare euismod ligula nec malesuada. Nulla facilisi. Nullam bibendum hendrerit pharetra. + +Duis pharetra auctor felis, eget aliquet justo commodo at. Donec quis nibh non arcu sodales efficitur. Aenean lorem sapien, tincidunt eu sapien nec, convallis tempor diam. Curabitur volutpat, felis ut congue euismod, lacus nisl euismod ipsum, vel consectetur orci nibh sed ligula. Curabitur consectetur vitae mauris sed tempor. Fusce vel pretium nibh, et tristique dolor. Aliquam semper a ante non finibus. Praesent euismod urna augue, at feugiat orci commodo ut. Duis imperdiet purus non augue cursus gravida. Maecenas sodales purus et sollicitudin venenatis. Nam ultrices lorem lectus, ut pretium turpis hendrerit eget. Vestibulum vel lacinia lectus, at dignissim diam. Vivamus ut tortor ac tortor blandit finibus. Etiam porttitor tortor sit amet elit lacinia gravida. Pellentesque pretium, orci vitae tempor vehicula, nisi tellus tincidunt sapien, id egestas felis quam a nunc. Pellentesque sed vestibulum nisl. + +Mauris congue, justo vel dapibus pretium, ipsum augue consectetur leo, at aliquam ipsum neque non mauris. Nam sollicitudin in urna eu blandit. Aliquam erat volutpat. Morbi eget interdum ante. Pellentesque congue gravida arcu, eget ornare ante elementum nec. Integer a auctor dolor, in varius sem. Etiam dictum nibh magna, at volutpat nunc blandit eu. Curabitur at sem ipsum. + +Nulla porttitor erat eget volutpat iaculis. Pellentesque egestas, nisl vel ultrices efficitur, ex magna condimentum urna, in tincidunt massa turpis eget metus. Etiam vitae magna elementum, fermentum justo ut, pretium velit. Aliquam aliquet venenatis malesuada. Quisque consequat enim metus, pellentesque blandit leo rhoncus id. Vivamus vestibulum, est in condimentum mollis, ligula eros blandit lorem, vitae dignissim lectus mi eget nulla. Cras posuere leo at neque convallis, sed pretium massa ultrices. Morbi tempus porttitor turpis. Nam vitae mauris et massa venenatis tincidunt. Quisque vestibulum lacus ligula, in sodales felis elementum vel. Sed a tellus condimentum, sodales nisi id, aliquet elit. + +Vestibulum ac ex eu turpis lacinia condimentum nec eget ipsum. Nulla non imperdiet erat, at malesuada lorem. Praesent efficitur imperdiet augue vel laoreet. Sed dignissim, ante non porta feugiat, enim nunc rhoncus lorem, eu luctus turpis risus sit amet orci. Vivamus bibendum pharetra venenatis. In at gravida nisi. Vestibulum pulvinar sapien ac augue scelerisque, vitae blandit nibh malesuada. Nunc vitae est faucibus, accumsan risus a, laoreet urna. Cras imperdiet lacus in augue facilisis dignissim. Duis sed nisi quis diam mollis rutrum. + +Vestibulum eget egestas neque. Praesent viverra, velit quis porttitor euismod, sem libero venenatis mauris, id congue urna nulla sed lorem. Nulla mollis metus nec diam malesuada aliquam. Duis ac risus nunc. Cras sollicitudin urna nunc, id sodales quam gravida sit amet. Fusce in vulputate orci, in venenatis lorem. Donec a sagittis ipsum. Quisque consequat sapien tellus, sed efficitur lacus aliquam eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean in neque at augue elementum commodo pellentesque eget ligula. Nullam condimentum efficitur tincidunt. Phasellus posuere tincidunt odio sed facilisis. Aenean eu risus at est euismod facilisis. Curabitur elit purus, malesuada quis blandit id, rutrum vitae dui. Praesent porta rutrum erat, ullamcorper viverra nunc. Cras ut velit dui. + +Etiam posuere pulvinar mi at ullamcorper. Pellentesque finibus, tellus non convallis commodo, orci nibh dapibus nisl, at aliquam purus nulla eget dui. Praesent fringilla urna nec nulla pellentesque, nec rhoncus turpis ultricies. Sed laoreet velit pellentesque libero varius, ac interdum urna viverra. Phasellus sed consectetur massa. Morbi quis velit nec ipsum varius tempor. Proin id sodales felis. Aliquam lacinia risus quis ligula condimentum sodales. Nulla vel arcu aliquet neque iaculis aliquet. Cras sed lorem eu turpis tincidunt sodales. Sed pulvinar elementum ligula, nec faucibus nisl. Fusce nec tellus eget dui tempor sagittis. In vitae enim in ex viverra commodo. Duis est erat, fringilla ac semper a, dapibus in tortor. + +Maecenas commodo vulputate iaculis. Aliquam non facilisis est. Donec pellentesque vitae nibh nec volutpat. In commodo metus placerat lorem commodo, non lacinia nibh bibendum. In viverra rhoncus erat. Mauris nec nisl blandit, elementum justo nec, accumsan libero. Aliquam elementum, velit et ullamcorper convallis, turpis lorem elementum lorem, quis consequat tortor eros ut erat. Curabitur et enim quis felis vulputate congue et vel purus. Sed elementum interdum ipsum, sed tincidunt arcu scelerisque et. + +Aenean interdum elementum mauris ut porta. Mauris vel purus ac odio vulputate pulvinar at quis odio. In turpis turpis, convallis in augue id, elementum vulputate lorem. Sed tincidunt fermentum vulputate. Nunc ipsum ipsum, molestie vel convallis id, pharetra vel arcu. Maecenas vel dui elit. Sed blandit dolor sit amet risus commodo faucibus. Duis rhoncus felis arcu, vel aliquam nisi faucibus sed. Suspendisse cursus eget nunc ut bibendum. Fusce non ligula risus. Curabitur vitae cursus metus, quis fringilla diam. Phasellus turpis ante, pulvinar ac turpis tristique, sollicitudin congue lectus. Proin quis ipsum at ipsum euismod euismod. + +Nunc ut fermentum nunc. Donec id commodo lacus, at hendrerit justo. Donec cursus purus sodales nunc commodo, quis bibendum elit hendrerit. Quisque quis pellentesque nibh, ac vulputate neque. Aenean non placerat felis, eget feugiat ligula. Vivamus a eros accumsan, cursus neque at, ultricies magna. Fusce tincidunt tellus vitae mi rutrum laoreet sed quis ligula. Nullam ullamcorper ligula ligula, vel fringilla metus aliquet a. Morbi aliquet, mauris vel interdum venenatis, ex arcu venenatis tortor, at tincidunt dui ipsum et arcu. Nulla blandit gravida nulla ac iaculis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ullamcorper lectus in sapien lobortis, eget posuere massa varius. + +Cras lacinia nunc faucibus mauris placerat pretium eget non sapien. Morbi viverra bibendum posuere. Aliquam accumsan sagittis dolor non iaculis. Sed nunc odio, lobortis in dolor ac, rutrum fermentum velit. In venenatis, velit in molestie semper, est magna condimentum dui, aliquam auctor lorem nulla vel ligula. Morbi vehicula turpis turpis, quis mollis justo tempus vitae. Proin luctus lacus in mauris porttitor aliquet. Duis vitae nunc ex. Nullam eget erat vitae nulla iaculis rhoncus. Sed lacus dui, suscipit eu leo vitae, tincidunt dignissim risus. Praesent ut massa ut arcu sagittis consequat. Sed sit amet tincidunt turpis. Nulla bibendum, felis eget posuere dictum, libero mi tristique elit, at venenatis neque elit ac quam. Curabitur nisi sem, scelerisque nec nunc ac, pulvinar pharetra odio. Curabitur egestas pellentesque arcu sed suscipit. In mattis dolor vel dui mollis feugiat. + +Sed commodo, dui ac vestibulum dictum, tellus libero tincidunt lacus, viverra commodo est felis vitae urna. Proin tincidunt neque vel turpis eleifend laoreet. Vestibulum sagittis, tortor sed iaculis consequat, urna ante sagittis est, ac ullamcorper lorem nibh dignissim odio. Nam arcu mi, cursus et blandit nec, aliquam ut nulla. Aenean quis iaculis tellus, eu egestas augue. Etiam pretium eget nisi quis iaculis. Aliquam sed convallis eros. Ut bibendum rhoncus lacus, in vestibulum dui ultricies id. Fusce vestibulum, mauris ut tempor consequat, dolor nisl pellentesque elit, in porta arcu elit vel ante. Vivamus at nisl est. Etiam nec blandit tortor, at pulvinar orci. Proin semper dapibus tincidunt. Phasellus lobortis enim ullamcorper dolor tempor cursus. + +Mauris a libero in enim gravida aliquet ac sit amet nibh. Vestibulum ac neque posuere, blandit libero ac, vehicula enim. Aliquam auctor iaculis eros sit amet molestie. Quisque faucibus turpis et massa tristique, nec dapibus mauris aliquet. Proin blandit aliquet mauris, non tincidunt odio blandit vel. Curabitur a nibh in eros commodo tincidunt eu et libero. Curabitur sit amet dapibus ex, in condimentum magna. Sed eu sem sem. Nunc tellus dolor, rutrum eu mauris nec, congue feugiat purus. Fusce tempor, neque vitae bibendum imperdiet, dolor ipsum condimentum urna, et egestas quam tortor in ex. Aliquam velit magna, commodo hendrerit sagittis sed, feugiat eget erat. Nunc quis ullamcorper velit, eu consequat augue. Nam arcu mauris, condimentum sit amet magna in, finibus scelerisque nunc. Mauris erat est, hendrerit ac accumsan eget, facilisis ut nisl. Quisque dignissim arcu quis diam tincidunt tristique. Sed rhoncus nisl non enim fermentum, a lobortis dolor consectetur. + +Sed eget condimentum ligula. Vestibulum vitae cursus eros. Donec elementum sapien magna, posuere iaculis sem ultrices lobortis. Morbi eu bibendum lectus. Suspendisse ante eros, ullamcorper ac viverra eget, pellentesque sed sapien. Duis sit amet tincidunt dui, vitae lobortis purus. Sed venenatis tincidunt volutpat. Vivamus a nisl ac elit consectetur semper ut eu libero. Proin id cursus ex. In hac habitasse platea dictumst. Aenean sed nisi vitae odio venenatis pulvinar vitae ac risus. Sed varius magna ut erat luctus vehicula. + +Nunc non ex eget purus blandit faucibus at quis velit. Donec quis mi vestibulum, facilisis nisi quis, tincidunt turpis. Sed bibendum metus sed consectetur mollis. Maecenas fermentum, erat finibus pulvinar lacinia, ex risus dictum sem, ut vestibulum augue diam vel diam. Donec ac massa non nibh pretium laoreet eget in orci. Nulla placerat eleifend mi, pretium vestibulum diam condimentum vitae. Nunc odio turpis, feugiat vitae turpis eget, pellentesque commodo turpis. Etiam sapien purus, consequat nec mi eget, consectetur efficitur neque. Phasellus porttitor sapien sit amet nunc semper, vel bibendum nibh finibus. Ut ac imperdiet ex, eu congue felis. In posuere nisi felis. Mauris tempus pretium mauris, ac viverra nunc hendrerit id. Sed fermentum nec sem ac pulvinar. Integer dictum velit eget congue venenatis. + +Cras eros dolor, venenatis ac dictum sed, dignissim nec sem. Curabitur tempor erat quis pretium interdum. Nunc vestibulum justo nisi, sit amet sagittis tellus consequat vel. Donec pharetra nunc vitae consequat eleifend. Quisque ut mauris quis nunc volutpat consectetur. Nam a suscipit ligula, at gravida libero. Vestibulum blandit, tellus sed bibendum volutpat, libero tortor convallis nisl, sit amet placerat lorem dolor nec libero. Integer blandit libero elit, ut congue ex euismod congue. Nulla sodales justo id eros condimentum faucibus. + +Proin quam ante, hendrerit at bibendum eu, pharetra at lectus. Proin finibus arcu id nisl aliquam dapibus. Fusce a suscipit nisl. Ut placerat ultrices nibh nec efficitur. Vestibulum vitae interdum magna. Donec lobortis finibus risus, et luctus ipsum efficitur euismod. Quisque dictum diam et venenatis euismod. Cras dictum molestie aliquet. Vestibulum imperdiet quam eget diam malesuada, quis pulvinar odio dignissim. Curabitur sapien elit, iaculis vitae justo eget, pretium malesuada elit. Donec gravida molestie tincidunt. Nullam at commodo ipsum. Sed nisi erat, tincidunt ultricies interdum consequat, pretium mollis ipsum. Vivamus in erat consectetur, sodales nibh at, porta nunc. Mauris semper, dui vitae condimentum tempus, mauris justo volutpat urna, ac ullamcorper tortor dolor in sem. + +Aenean leo ligula, egestas at vestibulum ac, porta vel mauris. Curabitur at lorem non mauris fringilla venenatis vel eu arcu. Donec posuere eleifend diam. Duis aliquet justo lacus, eu egestas erat eleifend sed. Sed non cursus urna. Sed pretium purus id blandit varius. Mauris turpis mi, lobortis eu tellus sit amet, maximus venenatis felis. Proin non varius enim, aliquet finibus velit. Praesent posuere pharetra ipsum eget varius. Phasellus non fermentum magna, ut iaculis augue. Praesent ut nisl nunc. + +Sed ligula tellus, interdum at sapien ut, dictum pellentesque nisl. Duis fringilla, sem nec elementum dapibus, nisl tellus maximus velit, eu varius sem nisl feugiat eros. Maecenas quis viverra lacus. Nulla nec commodo ex, nec placerat enim. Curabitur ultrices sagittis fringilla. Vestibulum sit amet enim sagittis, condimentum nisl sit amet, pulvinar orci. Ut at erat finibus erat bibendum convallis. Quisque euismod magna eget leo facilisis hendrerit. Cras venenatis, nisi quis sollicitudin volutpat, metus enim vestibulum nunc, at mollis leo leo nec est. Fusce fermentum tristique feugiat. Suspendisse sem est, condimentum ut ante at, egestas convallis ante. Vestibulum dictum, ex nec imperdiet laoreet, magna est ullamcorper augue, vel molestie quam odio vel ante. Donec dui dui, posuere a neque ac, condimentum vehicula magna. Curabitur suscipit, risus vitae bibendum posuere, mauris lorem viverra est, at tristique arcu quam quis leo. Donec sed posuere neque. Suspendisse iaculis aliquet condimentum. + +Morbi tempus condimentum diam eget bibendum. Aliquam varius magna quis lectus interdum, in elementum ligula tempor. Morbi vitae lorem sapien. Morbi luctus consectetur eros non aliquet. Pellentesque vestibulum sem sed ante accumsan faucibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ac suscipit justo. Nunc efficitur lectus eu arcu venenatis, vel accumsan ex suscipit. Vestibulum egestas ultricies tellus, et interdum enim pretium eu. Aenean rutrum est tincidunt rhoncus molestie. Phasellus hendrerit tellus et laoreet varius. Integer efficitur felis magna, nec sollicitudin arcu sollicitudin in. Curabitur non feugiat odio. Mauris nisi odio, luctus quis dolor sed, tristique luctus ex. Nullam libero enim, facilisis ut venenatis et, vulputate sed purus. + +Mauris a odio ut leo porttitor ultrices a et ex. Pellentesque vestibulum lacinia faucibus. In pellentesque eget augue at feugiat. Integer finibus augue dolor, in luctus lorem rutrum vitae. Donec pharetra lectus ac purus sollicitudin, vel tristique mauris pretium. Cras porttitor mi eu lectus consequat, a ultrices felis venenatis. In condimentum turpis in velit mattis laoreet. Aliquam sed mauris id nulla ultrices convallis vel vel velit. Suspendisse ut arcu finibus justo fermentum fringilla. Etiam in malesuada nisl. In hac habitasse platea dictumst. Duis a est lacinia, pretium dui eget, condimentum turpis. Integer ac placerat augue. Donec et eros felis. + +Integer cursus magna id quam sagittis consectetur. Aliquam erat volutpat. Quisque ullamcorper nisl nec massa dapibus facilisis vitae at nunc. Donec laoreet, libero in elementum tempus, enim odio porttitor felis, venenatis fermentum augue velit eu urna. Ut at ullamcorper enim. In molestie, velit et blandit maximus, erat nunc laoreet quam, vel finibus est mauris non sapien. Donec et dictum lorem. Nunc vestibulum, dolor eget tempus maximus, elit eros aliquam velit, vitae mattis mauris lectus ac tortor. Donec rutrum justo orci, id dictum metus vestibulum a. Etiam bibendum ipsum convallis, lacinia turpis vitae, dictum tortor. In velit dolor, scelerisque sed molestie nec, volutpat a turpis. Cras posuere commodo erat ut gravida. Quisque ante ipsum, volutpat a tellus non, dignissim ornare elit. Pellentesque sed porttitor dui, luctus rhoncus purus. In vitae ante pulvinar, consectetur orci quis, tempus velit. Curabitur tempus ligula id sapien ornare rhoncus. + +Maecenas eu nibh et elit accumsan blandit a at erat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam commodo feugiat condimentum. In non ante ut mauris eleifend lobortis. Phasellus eleifend vitae metus et semper. Nam sit amet rhoncus diam. Nunc molestie libero sed erat volutpat consequat. + +Aenean eget tristique odio. Vivamus quam tellus, dignissim sed faucibus sed, sagittis ut elit. Maecenas in ullamcorper sem. In at ipsum accumsan lectus vestibulum commodo nec non leo. Aliquam at suscipit felis. Nunc non egestas tortor. Donec sit amet eleifend eros. + +Sed eleifend nisi velit, in egestas erat condimentum eu. Pellentesque a tincidunt urna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum dapibus mauris vitae elit auctor, id venenatis sem consectetur. Vivamus non leo pulvinar, blandit libero ut, vehicula arcu. Nullam elementum ex enim, at mattis massa pharetra eu. Nunc nulla magna, lobortis a magna sit amet, laoreet fermentum justo. Curabitur aliquam sollicitudin posuere. Aenean semper porta dictum. Mauris accumsan non nisi nec faucibus augue. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in nibh eget ante viverra mattis. Etiam elit est, pharetra efficitur fermentum at, accumsan id eros. Aenean accumsan nisi facilisis tempor suscipit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam magna erat, tincidunt eu placerat at, molestie at sem. In sit amet cursus mauris. Etiam ullamcorper lacus nisi, et porta metus semper id. Nulla mauris nunc, pharetra at facilisis a, consequat id metus. Sed convallis ligula non felis vulputate finibus. Curabitur nisl nulla, pharetra in justo a, dapibus ultricies dui. Morbi ultricies sollicitudin purus, quis pellentesque leo rhoncus sed. Curabitur sed eros quis lacus vulputate pretium. Vestibulum vitae urna nec arcu vulputate efficitur. Cras venenatis sodales dolor non ullamcorper. + +Donec eu placerat magna. Vestibulum justo lacus, luctus ac luctus sit amet, dapibus at orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec eu tortor ac justo pharetra hendrerit. Phasellus ornare lacinia vestibulum. Maecenas diam orci, molestie rhoncus fringilla vitae, aliquam eu purus. Maecenas vehicula felis dui, vel dapibus odio lobortis quis. Cras a velit non nisl efficitur tempor. + +Suspendisse magna enim, ultrices et elementum ut, venenatis ut purus. Curabitur convallis vel tortor id rhoncus. Pellentesque varius arcu non imperdiet eleifend. Vestibulum fringilla aliquet vehicula. Nullam et lorem ullamcorper, tincidunt ante eget, egestas ex. Donec laoreet, justo id tincidunt egestas, lectus diam faucibus tortor, nec venenatis felis leo a urna. In at tortor semper augue finibus ornare et vitae erat. Praesent vulputate, dui sed pulvinar porta, justo tortor feugiat tellus, eget accumsan velit turpis at orci. Nulla vitae velit facilisis augue sagittis fringilla nec sagittis nunc. + +Duis vel elementum odio, id eleifend mauris. Nam in ultrices nulla. Quisque pharetra blandit risus eget lacinia. Nulla mattis mi felis, a ultrices nisi egestas vulputate. Donec a augue ac ex laoreet semper at porta ante. Vestibulum arcu ipsum, vehicula vel mollis lobortis, ullamcorper sed augue. Ut viverra faucibus nunc, sit amet sodales diam gravida sollicitudin. Fusce finibus quam sed ornare consequat. Suspendisse viverra ultricies dui in volutpat. Maecenas blandit erat in rhoncus placerat. Phasellus eu nisi rutrum, bibendum nunc eu, viverra ex. Praesent sollicitudin mi sit amet dictum convallis. Vivamus purus ligula, interdum at tincidunt sit amet, pellentesque ut elit. Aenean vitae ultrices ex. In facilisis lectus est, nec vulputate diam tristique vel. In gravida tincidunt ex et viverra. + +Pellentesque mi justo, dignissim a nisl quis, tempor dictum lacus. Nam tristique varius dolor tincidunt pharetra. Nullam iaculis est sed quam dignissim cursus. Duis sit amet vehicula nisi, sed consectetur velit. Nullam non tellus nec dolor venenatis porta quis at quam. In pharetra id orci sed faucibus. Cras et malesuada erat. + +Quisque fringilla commodo metus nec feugiat. Donec et est urna. Maecenas ut magna dui. Vivamus eu sem venenatis, porta mi at, efficitur tortor. In lacinia hendrerit elit, in faucibus nulla ultrices et. Mauris accumsan nec orci et vestibulum. Aliquam eget justo velit. Mauris fringilla ullamcorper enim, in elementum enim eleifend a. Nulla id libero ac arcu tristique ultrices. Maecenas mattis orci vitae nisl laoreet auctor. + +Quisque id aliquam orci. Nunc molestie vitae quam eu consectetur. Vivamus molestie venenatis est, nec lacinia odio faucibus eget. Etiam ornare eu leo eget posuere. Quisque elementum ligula at euismod placerat. Maecenas lobortis leo diam, vel egestas odio iaculis ac. Integer dapibus mi metus. Sed at eleifend ligula, ullamcorper vestibulum eros. Suspendisse dignissim metus in vulputate iaculis. Nam rhoncus felis sit amet velit scelerisque semper. Ut sed augue eget nulla laoreet scelerisque. + +Aenean convallis ipsum id turpis gravida, et elementum ante facilisis. Donec vitae arcu id mauris mollis hendrerit. Mauris imperdiet cursus nibh at laoreet. Sed sit amet enim magna. Mauris blandit nisi quis arcu sodales, eget consequat orci euismod. Curabitur id bibendum diam. Ut pharetra tellus nec enim tristique, id efficitur eros accumsan. Suspendisse potenti. Praesent vel porttitor risus. Nulla vitae ex nec ex hendrerit euismod non mattis arcu. Aenean eget velit massa. Pellentesque venenatis arcu mauris, sit amet scelerisque sem lobortis ornare. Vivamus auctor porta eros, eget blandit lorem semper non. Nunc sed nibh commodo, hendrerit nunc at, malesuada nisl. Aliquam pretium leo sed iaculis vehicula. Vivamus interdum porta augue. + +Suspendisse potenti. Aenean sodales vehicula erat, vel sollicitudin eros eleifend id. Suspendisse hendrerit malesuada est, imperdiet tristique ex aliquet ac. Vivamus ultricies placerat lorem, ac placerat lectus sollicitudin ac. Etiam consequat odio vel bibendum pretium. Integer elementum nisl magna. Nam et vehicula risus, sed mollis tortor. Ut in molestie turpis. Nam luctus, elit molestie varius luctus, lacus ligula ullamcorper elit, non interdum ipsum neque non nibh. Curabitur vulputate facilisis suscipit. Sed vitae augue eu ex luctus lacinia ac vitae quam. Quisque non nibh ex. Praesent gravida efficitur dui, a vulputate nulla ultrices vitae. Duis libero dolor, facilisis in tempus vitae, pharetra non libero. Sed urna leo, placerat quis neque at, interdum placerat odio. Interdum et malesuada fames ac ante ipsum primis in faucibus. + +Vivamus in hendrerit elit, quis egestas sem. Sed tempus dolor finibus massa ultrices, sed tristique quam semper. Pellentesque euismod molestie facilisis. Vivamus sit amet ultrices elit. Ut eleifend, libero eu molestie maximus, ipsum elit pulvinar tortor, et aliquet nulla orci eu est. Nullam pretium, ligula at faucibus gravida, velit mauris pulvinar felis, sit amet tincidunt magna dui ut quam. Phasellus porttitor eu nunc id maximus. Suspendisse volutpat suscipit porta. + +Integer dictum augue purus, sed cursus eros blandit id. Vestibulum non nibh viverra, molestie lectus eu, vestibulum justo. Nullam a libero non nibh dignissim posuere id vel orci. Nulla viverra lorem eget condimentum scelerisque. Donec porta nunc sed sapien pretium dapibus. In hac habitasse platea dictumst. Suspendisse sit amet ligula elementum, accumsan ipsum vel, imperdiet massa. Fusce scelerisque non erat ac bibendum. Pellentesque consequat vehicula euismod. Morbi sapien nisl, ultrices ut scelerisque consectetur, ornare et orci. Maecenas efficitur eros a venenatis rutrum. Aenean bibendum dui in enim luctus posuere. Donec placerat porta eros eu dignissim. Fusce nulla velit, sodales eget nibh gravida, tincidunt venenatis urna. Aenean condimentum, massa in fringilla lobortis, turpis felis lacinia metus, sit amet facilisis nisl quam aliquet diam. Praesent fringilla vitae arcu nec lobortis. + +In tincidunt nisi ac dictum cursus. Sed dolor purus, posuere vel dolor vel, semper tempor elit. Integer nec scelerisque tellus, sit amet dictum magna. Donec hendrerit aliquet libero, a varius velit dapibus quis. Donec lectus turpis, egestas vel vestibulum vitae, iaculis quis eros. Maecenas id convallis metus. Duis quis tellus iaculis, lobortis massa vitae, condimentum eros. Pellentesque scelerisque nisl id hendrerit tempor. Donec quam augue, maximus eu porta ut, venenatis a ante. Curabitur dictum et nibh sed auctor. Nam et consequat odio. In vitae magna a dui porta pellentesque quis id magna. Sed eu nunc bibendum, imperdiet nunc sit amet, cursus diam. Vivamus in odio vel nibh sollicitudin molestie. Morbi sit amet leo et odio congue pharetra. Aliquam quis suscipit orci. + +Donec at ornare orci. Suspendisse nec tellus elit. Nam erat urna, laoreet at elit sed, tristique interdum ligula. Nullam dapibus orci sed lorem convallis fringilla. Cras urna ipsum, tristique maximus nisl quis, auctor mattis quam. Donec finibus consectetur lectus et interdum. Aenean posuere lectus vel turpis auctor finibus. Praesent molestie lectus ipsum, et tristique quam porttitor ac. Suspendisse aliquam pretium tortor, id egestas erat. Nulla mollis, orci at semper vulputate, nisl est pharetra diam, sit amet vulputate diam augue a sem. Donec in mauris felis. + +Nunc eleifend at elit a volutpat. Integer semper quis quam at faucibus. Donec pretium purus vitae erat pretium posuere. Sed ac aliquet nulla. Nam ut sagittis mauris. Sed quis sagittis risus, id pretium urna. Nam sagittis elementum ornare. Aenean ligula sapien, congue eget mi quis, viverra commodo nibh. Suspendisse non iaculis magna, nec volutpat neque. Donec eu dapibus eros. + +Nam sit amet iaculis massa. Nullam vel laoreet tellus, id cursus tortor. In hac habitasse platea dictumst. Curabitur in urna risus. Proin dui sem, interdum accumsan cursus ut, dignissim in purus. Nunc vitae consequat arcu. Proin volutpat elementum quam in posuere. Pellentesque luctus arcu in ornare tempor. Cras est turpis, viverra vel consectetur ac, dictum a quam. Donec sagittis tortor sed volutpat accumsan. Curabitur a tellus vitae arcu pretium viverra vel in ligula. Pellentesque turpis augue, porta vitae quam id, fermentum congue leo. Sed nec fermentum turpis. Nulla vehicula vulputate sem eu consequat. In tincidunt nisi et mi maximus, non facilisis justo porttitor. Proin eu sapien aliquam, accumsan nunc non, pulvinar leo. + +Nam vitae commodo sem. Ut laoreet in quam in suscipit. Nullam at faucibus diam. Fusce ut purus at ex lobortis elementum vitae quis sapien. Nam tempus consectetur ex, sed luctus felis. Donec porttitor mi dui, non luctus quam consectetur eu. Ut a egestas diam. Etiam sodales, nisl vitae gravida pulvinar, libero est condimentum tellus, vitae ullamcorper tortor justo a urna. Maecenas ac velit accumsan, laoreet leo eget, elementum libero. Maecenas dictum tincidunt blandit. Proin sed bibendum urna. Phasellus fermentum tincidunt tempor. Mauris iaculis, mi ac fermentum interdum, nibh odio pellentesque nunc, vel scelerisque sapien sem id purus. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas imperdiet sit amet lectus at hendrerit. Vivamus luctus, elit non lacinia hendrerit, risus velit finibus nulla, quis sagittis ipsum diam ut odio. Sed mauris sapien, vehicula efficitur gravida sed, gravida in lectus. Fusce eget consectetur turpis, in fermentum neque. Nullam turpis turpis, feugiat a accumsan in, euismod a augue. Vestibulum nec neque libero. Phasellus eget efficitur turpis, fermentum molestie nunc. Sed consequat elit urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Nunc egestas consequat blandit. Donec quis porta sapien. Sed augue nunc, efficitur vitae tellus sed, finibus bibendum sem. Fusce id sem quis quam pharetra ultrices. Phasellus non convallis velit. Quisque erat tellus, pretium eget porta in, ornare a arcu. Aenean nec lectus lorem. Suspendisse dolor lectus, ullamcorper ornare lorem a, consequat lobortis elit. Nunc dignissim est nec gravida facilisis. Proin faucibus erat vel eros volutpat, in vulputate neque sodales. + +Nunc vitae imperdiet ipsum, faucibus vehicula magna. Nulla nec nisl sapien. Curabitur et dui eget tortor efficitur accumsan. Aenean in pellentesque erat. Nam condimentum neque pulvinar, aliquam nisl eu, mollis lorem. Integer rutrum arcu eget felis semper euismod. Quisque vestibulum vel diam a tempor. Aenean mollis, tellus sit amet pretium pulvinar, nulla dolor ornare ligula, eget dignissim orci tortor ut sapien. Nam ut ipsum id lectus venenatis tempus vel non ex. Suspendisse blandit vitae nunc vitae porta. Suspendisse tincidunt est sit amet ultricies consectetur. Nulla fermentum hendrerit ex, vehicula rhoncus arcu lobortis ut. Vestibulum fermentum ornare diam at pellentesque. Praesent nunc lorem, porta et magna nec, sodales commodo justo. Duis aliquam sapien et rutrum tempus. Vestibulum malesuada felis eu ligula posuere luctus. + +Maecenas lacinia, lectus eget rhoncus aliquam, tortor est gravida sapien, vel aliquam arcu erat et magna. Praesent fringilla leo eget neque posuere imperdiet. In porttitor elit non enim gravida euismod. Aliquam tempus, orci at interdum dapibus, mauris lacus egestas sapien, ac ullamcorper ex nibh sed orci. Quisque iaculis enim et lectus egestas, malesuada posuere lectus interdum. Sed dignissim neque vel turpis dictum ornare. Vestibulum suscipit consequat maximus. + +Ut et ante sit amet leo rutrum volutpat. Sed malesuada quis sapien et ornare. Aliquam ac ex enim. Curabitur vel quam et orci posuere feugiat. Pellentesque nec metus eget sapien eleifend tincidunt non sit amet arcu. Cras posuere metus eget risus varius fermentum. Nullam orci eros, efficitur nec sapien nec, pretium laoreet erat. Nam gravida purus mauris, ac viverra orci hendrerit sed. Aenean ligula massa, posuere id faucibus vitae, malesuada quis augue. Morbi consectetur mattis mi, quis sodales diam bibendum eget. Aliquam sagittis neque at feugiat posuere. Donec gravida lectus a lectus fringilla tincidunt. Vivamus volutpat dui et turpis condimentum, ut tincidunt tortor lacinia. Ut laoreet, ante in pellentesque sodales, elit mauris scelerisque dui, quis tincidunt quam massa ac enim. Nulla porta porta sapien vel sollicitudin. + +Phasellus sed lectus posuere, mollis ante non, feugiat odio. Aenean a quam id mi ornare dapibus. Donec venenatis ipsum non velit accumsan, id elementum dolor imperdiet. Phasellus lacinia erat diam, sit amet convallis lectus ornare in. Curabitur lorem ligula, maximus eu varius quis, auctor quis odio. Etiam in orci porttitor, sodales ipsum at, rhoncus turpis. Nulla eget luctus nisi. Duis convallis est eget ligula fringilla viverra. Duis nec sapien quis dui luctus ornare sit amet quis erat. Quisque nec justo dui. + +Sed eleifend, tellus quis laoreet rhoncus, leo turpis imperdiet turpis, pretium varius odio tortor et leo. Morbi ut mi fringilla, porttitor sem ac, accumsan ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum cursus dolor accumsan dolor aliquet dignissim. Duis sed feugiat erat. Donec sed arcu accumsan, ornare nisl eget, lobortis nibh. Praesent pulvinar quis justo et hendrerit. + +Sed velit nibh, efficitur ut leo sed, eleifend iaculis nisl. Mauris ac diam euismod, luctus enim maximus, tincidunt sem. Ut tempus magna vitae blandit bibendum. Sed sapien tortor, ultrices et justo vel, pharetra faucibus dui. Vivamus et vestibulum arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris tincidunt suscipit risus, vitae varius felis commodo in. Nullam semper tortor dolor, sit amet pharetra odio interdum hendrerit. Pellentesque sed justo eros. Cras quam purus, eleifend id tellus molestie, eleifend finibus lorem. Donec a volutpat libero, at vehicula tellus. Vivamus tellus orci, pharetra in nisi vitae, tincidunt dapibus nunc. + +Suspendisse ultricies sem laoreet quam molestie ullamcorper. Sed auctor sodales metus, eget convallis eros ultrices quis. Duis rutrum mi a tortor iaculis posuere. Suspendisse potenti. Pellentesque dictum faucibus dui a vestibulum. Donec elit nisl, aliquet at dolor non, viverra dignissim felis. Donec mi elit, condimentum sit amet magna id, efficitur sodales arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque est elit, porttitor eget bibendum nec, auctor nec nulla. Pellentesque dignissim nisl vel orci commodo, ut ullamcorper justo viverra. + +Suspendisse pharetra gravida turpis sit amet efficitur. Nulla mattis tortor eget pharetra ultrices. Ut a turpis maximus, pharetra justo non, tempor quam. Nam et volutpat lectus. Vestibulum congue turpis augue, quis eleifend nulla euismod in. Vestibulum sed erat tempus, porttitor diam vel, elementum metus. Aenean facilisis molestie ante, sit amet ornare lacus mollis sed. Maecenas rutrum urna nec ex malesuada consequat. Nunc interdum pellentesque nisl sed viverra. Nam nec fermentum ipsum. Nulla facilisi. Maecenas congue dolor erat, a fermentum enim congue vitae. Integer metus libero, feugiat at eleifend eu, iaculis nec leo. Praesent eleifend convallis leo, eu posuere urna elementum eu. + +Aliquam dapibus dolor vel urna luctus venenatis. Suspendisse potenti. Donec vitae tellus nisi. Pellentesque vel neque dignissim, ornare tellus sed, sodales metus. Aliquam vitae maximus tortor. Nullam mattis, odio id porttitor euismod, est leo aliquet massa, bibendum pulvinar justo orci a magna. Morbi ut nisl congue, porttitor erat non, gravida turpis. In nulla risus, ullamcorper quis suscipit vel, tristique ut nulla. In malesuada odio augue, ac hendrerit nunc mattis a. Nam in quam ut elit ullamcorper mattis. Sed fringilla tempus felis, id pretium tortor varius non. Aenean quis sem quis risus mattis posuere vel quis nibh. + +Sed pulvinar commodo dui sit amet malesuada. Quisque porta tellus placerat congue efficitur. Cras id blandit enim. Praesent a urna felis. Aliquam ornare, nisl eget iaculis tempor, ligula mi vehicula dolor, ut eleifend massa massa vel est. Fusce venenatis laoreet lobortis. Proin tempus aliquet nunc ut porttitor. In est mauris, blandit eu mattis vitae, aliquam a orci. Cras vitae nulla nec ex cursus blandit. Aliquam fermentum, erat in aliquet dictum, metus urna fermentum quam, non varius magna lectus non urna. + +Donec faucibus nisl nunc. Integer rutrum dui enim, malesuada semper turpis pulvinar eget. Fusce consectetur ipsum tellus, sed maximus lorem auctor in. Morbi nec est in quam ultricies hendrerit. Sed aliquam sollicitudin elementum. Aenean aliquam ex sit amet dignissim venenatis. Duis malesuada leo nisi, id accumsan turpis pretium id. Sed ornare magna non pharetra pharetra. Ut auctor dolor neque, nec bibendum odio viverra in. Nulla convallis interdum condimentum. Proin vestibulum turpis in lorem ultrices, bibendum tristique ligula faucibus. In tincidunt auctor sem, vitae fringilla neque pulvinar eu. Etiam commodo pellentesque enim. Phasellus feugiat non sapien eget sollicitudin. Etiam consequat efficitur lacus vel maximus. Suspendisse vitae elementum diam. + +Mauris urna velit, efficitur at viverra at, interdum a velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean bibendum sagittis massa in interdum. Mauris facilisis dui eget ipsum euismod ultrices. Donec sit amet lorem iaculis, condimentum velit quis, lacinia justo. Integer faucibus metus sed eros semper, in congue tortor venenatis. Proin tincidunt maximus enim, accumsan facilisis tellus gravida eu. Phasellus interdum aliquam ante, sit amet rhoncus nisl ornare at. Suspendisse libero eros, cursus quis fermentum nec, tempor eget lectus. Aenean ullamcorper augue lacus, non iaculis dui rutrum id. Aliquam erat volutpat. + +Morbi hendrerit fermentum sodales. Proin rutrum congue auctor. Mauris pellentesque elit non velit condimentum tincidunt a sit amet velit. Etiam accumsan ante id neque commodo vulputate. Aenean nec mattis neque. Aliquam tempor urna quis nisl convallis congue. Praesent vitae porttitor ante. Mauris mattis vestibulum ante, nec auctor augue. Praesent sem leo, accumsan eu fermentum egestas, iaculis ac nulla. + +Etiam vel nisi congue, varius neque id, volutpat quam. Fusce placerat arcu hendrerit orci condimentum vehicula. Integer sem ex, facilisis et lacinia a, condimentum at ante. Morbi condimentum diam tellus, non lobortis arcu pellentesque sit amet. Phasellus imperdiet augue eget sollicitudin mollis. Vivamus sollicitudin arcu aliquam lectus tristique, in consequat diam egestas. Suspendisse aliquet non velit id feugiat. Sed eu est fringilla, gravida nulla ac, dignissim tortor. Phasellus pellentesque nisl non venenatis sagittis. Etiam facilisis nulla quis lorem scelerisque vehicula id at ex. Duis in risus enim. In eu vulputate massa, vel dignissim odio. Praesent ut mi tellus. In hac habitasse platea dictumst. + +Nunc malesuada turpis in fermentum lobortis. Donec blandit eu orci non laoreet. Suspendisse posuere blandit tortor, in accumsan velit cursus in. Integer suscipit justo nulla, in viverra orci suscipit et. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum a pulvinar lacus. Vestibulum mattis nisi vel nunc convallis maximus. Fusce aliquam, erat in volutpat gravida, elit odio feugiat ante, nec varius enim leo eget nisl. Donec sit amet ligula lobortis, sollicitudin dolor quis, blandit augue. Duis at interdum sem. Nullam eleifend ligula urna, vitae sollicitudin purus cursus nec. + +In commodo risus eu justo hendrerit, ut posuere mi eleifend. Suspendisse sollicitudin odio sem. Vestibulum at dapibus dui, vel dictum nisi. In hac habitasse platea dictumst. Sed ac pharetra ex. Ut ultrices augue ut vulputate condimentum. Phasellus convallis arcu tortor, ac tincidunt justo cursus vitae. Mauris dignissim dapibus imperdiet. Nullam id quam eget mauris cursus molestie finibus eu enim. Fusce laoreet orci eu nunc fermentum tincidunt. Pellentesque vitae ex ac nunc porta mattis sed ut ligula. Phasellus erat dui, consequat et lacinia vel, blandit nec dui. Donec ipsum magna, rhoncus ac viverra vitae, feugiat vel ligula. + +Cras ut nisl sed elit dictum semper a at magna. Aliquam laoreet viverra velit vel lobortis. Nam tempor lorem sit amet purus tincidunt accumsan. Vestibulum et vestibulum ligula. Donec sit amet neque faucibus leo rutrum semper. Maecenas scelerisque, lacus et lobortis congue, purus quam euismod risus, et mattis orci nisl eget est. In hac habitasse platea dictumst. Pellentesque eros velit, sollicitudin quis ante vel, blandit maximus mi. Suspendisse at eros id quam vulputate ornare. Duis placerat tellus vel odio ultrices, ac feugiat enim placerat. Vestibulum ut massa mattis, commodo orci euismod, cursus lacus. Suspendisse potenti. Sed venenatis cursus neque, at lacinia erat ornare et. Sed rutrum, dui at porttitor hendrerit, lacus magna fringilla quam, id mollis elit leo ut ante. Praesent vel diam sed urna mollis laoreet eget ut risus. + +Mauris varius odio lectus, sit amet consequat nulla sollicitudin sed. Suspendisse commodo rhoncus enim vitae pellentesque. Aliquam vulputate sollicitudin aliquet. Mauris interdum interdum orci, non varius sem ornare ut. Sed vel nibh nunc. Duis tincidunt quam quis lectus egestas, eu ultricies ante posuere. Aenean in mauris eros. Suspendisse potenti. Vivamus sit amet augue at velit accumsan fermentum. Phasellus vel dui sit amet felis convallis sodales. + +Nunc mattis vitae sapien ut dignissim. Nam fermentum sit amet massa eu accumsan. In ut ipsum sit amet ante pellentesque accumsan. Nulla egestas eros eget lacus rhoncus pharetra. Nam pellentesque laoreet ex in lobortis. Vivamus congue tincidunt molestie. Integer vel turpis augue. Cras vel molestie quam. Etiam vel mauris ut tellus finibus consequat. + +Curabitur velit erat, vestibulum blandit massa a, aliquam elementum tellus. Pellentesque id nisl tempor lorem condimentum dapibus. Quisque ut lorem at orci elementum dapibus quis ut enim. Praesent venenatis congue arcu eu sagittis. Nunc nunc massa, posuere ac gravida id, scelerisque nec velit. Suspendisse non lacus a orci dapibus congue. Sed leo velit, facilisis sed egestas ut, vehicula at turpis. Praesent a finibus felis. Quisque est mi, pellentesque sit amet varius eu, gravida id est. + +Donec auctor gravida urna ac suscipit. Etiam placerat ipsum vitae ante congue, at fringilla lectus ullamcorper. Donec viverra lacus ut erat posuere, dignissim porta purus tempor. Duis eget enim quis diam vehicula pulvinar. Donec tempus eros velit, sit amet pharetra ligula placerat in. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet pretium tellus, non dictum ligula. Nullam a ex purus. Vestibulum id ex rhoncus, pretium eros vel, consequat tellus. Vivamus lacinia dolor nec ipsum aliquam, euismod fermentum sem efficitur. Nulla facilisi. Pellentesque pharetra lacinia augue, et eleifend lorem aliquet eu. Ut ut porta ante. Duis ut auctor nisi, id porttitor erat. Proin sollicitudin sem quis justo sodales aliquam. Nulla pharetra gravida arcu vel tempus. + +Maecenas nec imperdiet nulla, vitae fringilla diam. Mauris maximus aliquet tellus at congue. Aliquam in dictum elit. Sed pretium mattis lectus, non pellentesque enim dignissim vel. Sed quis elementum diam, a porttitor enim. Cras cursus eros nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ornare sapien vel diam vestibulum, at commodo metus lobortis. Sed euismod iaculis scelerisque. Pellentesque venenatis malesuada dolor, at tempus eros venenatis sit amet. Fusce a massa non libero blandit suscipit. + +Nulla facilisi. Sed nec leo nisl. Proin condimentum nunc at risus semper, in laoreet dui congue. Integer in dolor vitae ex maximus porttitor. In efficitur vulputate metus, ac egestas diam pharetra ac. Sed tristique tempus ligula dignissim convallis. Ut tincidunt laoreet fringilla. Praesent nunc est, lacinia tristique odio in, varius rhoncus ipsum. Vivamus bibendum justo at metus ullamcorper imperdiet. Quisque elit nibh, rhoncus a placerat eget, vehicula quis ante. + +Morbi fermentum turpis enim, eu interdum dui interdum sit amet. Sed vulputate lacus nec ligula gravida euismod. Duis in nisl fringilla, sollicitudin diam ac, laoreet tortor. Sed eget porttitor augue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed eu enim convallis augue vulputate convallis. Praesent laoreet, leo at ornare luctus, nisi felis semper sapien, sit amet sodales augue mauris eu lorem. Fusce ornare volutpat malesuada. Curabitur vehicula, eros id fringilla placerat, tellus elit venenatis lorem, ac imperdiet purus ex blandit felis. Nunc suscipit elementum risus ac dictum. Aliquam bibendum dignissim ipsum, sit amet posuere sem viverra ut. Vestibulum egestas in ex ac volutpat. + +Donec ut faucibus velit, nec suscipit metus. Nullam dui ligula, commodo eget est venenatis, ullamcorper porttitor lectus. Suspendisse dictum, metus vitae commodo ultricies, enim quam pretium enim, a efficitur tortor purus sed ipsum. Nam sit amet lacus vel elit consectetur ultrices. Vestibulum ac nibh in metus porttitor vestibulum. In blandit et est non efficitur. Aenean vestibulum tortor sit amet mattis semper. Praesent sit amet turpis ac neque malesuada tincidunt nec nec urna. Mauris posuere elit nisl, ac ullamcorper nisi pulvinar in. Nulla ac elit in neque ullamcorper facilisis sed et dui. + +Quisque molestie euismod dui, non scelerisque arcu dictum a. Donec eu nulla nisl. Aliquam neque orci, ultrices in nunc vitae, sollicitudin eleifend augue. Etiam at mattis velit, a efficitur dui. Nam bibendum enim non elit tristique aliquet. Vestibulum eget nibh lacus. Pellentesque id sapien dui. Nullam urna leo, faucibus et efficitur vel, ullamcorper iaculis mi. Donec sit amet bibendum justo, id dapibus sem. Pellentesque dignissim varius tellus nec egestas. Praesent eu orci vel ipsum pharetra maximus. Cras nisl ligula, sollicitudin in ligula vel, posuere sagittis eros. Phasellus porta tristique mauris vel eleifend. Mauris sit amet volutpat ipsum, sed vestibulum orci. + +Ut rutrum augue quis rhoncus tincidunt. Aenean sollicitudin lacus at erat varius ullamcorper. Integer vitae orci vel nisi vulputate cursus eget nec ex. Mauris elementum, augue ac accumsan convallis, libero leo porta ante, sit amet elementum felis ligula non enim. Ut venenatis semper posuere. Vestibulum nec nisl nisi. Ut a sapien ac orci finibus dictum sed at ex. Phasellus ac lorem nisl. Quisque vitae tempor lacus. Vestibulum in mauris diam. Proin mattis ligula vitae ipsum bibendum, at dapibus nunc placerat. Nam iaculis justo in accumsan tristique. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris pretium velit et tristique pellentesque. Nunc in sapien a purus congue rutrum. Nam placerat risus in ante rutrum rutrum. In in ligula magna. Duis orci ante, vehicula elementum consectetur vitae, lobortis vitae arcu. Ut feugiat tempus metus quis sollicitudin. Etiam cursus venenatis augue at fermentum. + +Vestibulum id vehicula massa. Proin ut ligula et sapien placerat tristique non nec nibh. Etiam non lacus placerat, molestie ex eu, venenatis elit. Sed posuere, ante et ullamcorper luctus, elit lectus blandit tortor, nec consequat massa elit a tortor. Fusce urna felis, porttitor at ultrices vel, eleifend porta ipsum. Pellentesque nisl nibh, molestie sit amet sem eu, dapibus pharetra nulla. Phasellus viverra augue eu augue volutpat dignissim. Integer bibendum faucibus varius. Curabitur non turpis purus. + +Sed pretium eros nisl, sed ullamcorper magna faucibus quis. Etiam ornare euismod tellus, vitae accumsan eros bibendum ut. Morbi a ex sed risus faucibus rutrum et ut urna. Nullam non tortor commodo, facilisis massa non, tristique metus. Curabitur placerat, arcu in egestas ullamcorper, nisl nisl luctus felis, ac dapibus erat velit sed erat. Proin sagittis felis vitae sem posuere semper. Vivamus fermentum eu nisi ac facilisis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque pellentesque lacinia ex tempus lacinia. Aliquam erat volutpat. Nam aliquet mattis risus non pretium. Suspendisse potenti. + +Suspendisse et sapien ornare, elementum erat vel, ultrices nisi. Curabitur interdum dignissim tincidunt. Cras eget est a velit consectetur finibus et sed nisl. Curabitur vestibulum semper posuere. Aliquam porta diam commodo nulla tempus imperdiet. Sed id dictum neque. Ut sit amet risus aliquet, dignissim velit sed, tristique ipsum. Nam a sapien id magna commodo tincidunt quis ac tellus. Nunc nec pellentesque orci. In justo purus, vulputate quis urna a, fringilla blandit sem. Cras porttitor quam vitae metus vehicula faucibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut eleifend orci lectus, vitae semper eros hendrerit id. Fusce nec tellus condimentum, vehicula elit quis, gravida erat. Maecenas volutpat interdum velit id vestibulum. + +Pellentesque id metus metus. Nullam ac metus non nisi facilisis aliquet quis a sem. Morbi cursus consectetur aliquam. Morbi id sapien nibh. Etiam tempus tempor tempor. Nam scelerisque condimentum purus. Proin nec cursus eros, in condimentum dolor. Nam in sagittis urna. Ut eget ornare erat. Vivamus nec elit ut sapien tristique aliquet a nec urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas varius pellentesque diam. + +Ut eget libero iaculis urna porta iaculis vel ac eros. Sed sed mi et libero porta consequat a in libero. Sed in imperdiet ipsum, sit amet ultricies dui. Curabitur sit amet consectetur eros. Praesent nunc tellus, feugiat ac laoreet consectetur, accumsan nec magna. Praesent at elementum orci. Donec dapibus venenatis libero, id tincidunt libero euismod ac. + +Aenean sit amet ex placerat, molestie sapien a, volutpat turpis. Vivamus elementum lacinia nisi a convallis. Donec a sagittis turpis. Curabitur pulvinar laoreet dolor id consequat. Aliquam aliquam feugiat magna, vitae sagittis lorem mattis ut. Donec sed auctor nulla. Ut convallis, neque vitae faucibus efficitur, nisi justo pharetra odio, vitae tristique purus lorem et nisi. Quisque eleifend aliquam arcu nec maximus. Nunc mauris elit, finibus nec gravida non, maximus nec nibh. Sed eu ipsum in arcu gravida maximus. Maecenas massa dolor, ullamcorper eget odio eu, congue ornare leo. Suspendisse venenatis ultricies imperdiet. Nam finibus orci vitae sagittis finibus. Pellentesque sit amet dapibus diam. Nam eu nunc elit. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam euismod turpis nec urna lobortis, ut malesuada ex suscipit. Fusce sed viverra risus. Pellentesque tristique nulla pulvinar tincidunt accumsan. Nullam vitae nibh imperdiet erat rutrum pellentesque et sit amet quam. Aenean laoreet ultricies hendrerit. Duis elementum tellus a feugiat vulputate. Aliquam aliquam enim placerat dolor viverra, non suscipit lorem ultrices. + +Proin interdum vitae lorem quis viverra. Praesent nec tempor dolor, vitae sagittis turpis. Etiam sit amet imperdiet sapien, ac laoreet arcu. Proin aliquam, risus nec maximus dapibus, dui diam elementum dolor, vitae tempor augue lorem vel justo. Aenean ac egestas dolor. Ut aliquet, felis at fringilla venenatis, leo nisl ullamcorper ex, vitae pellentesque turpis orci quis lectus. Nullam vitae suscipit metus. Integer congue, ante in lacinia rhoncus, erat lorem interdum elit, egestas suscipit lectus nunc non orci. Nunc vel viverra quam. Mauris sit amet sodales tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis sollicitudin libero. In nec venenatis orci. + +Integer non quam fringilla, tristique nulla id, gravida arcu. Aenean scelerisque lacinia magna. Praesent nunc sem, lobortis non convallis rhoncus, rutrum vitae ante. Sed et orci ut erat viverra bibendum a et lectus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce sit amet eros eget dolor pharetra vestibulum. Nullam vestibulum, massa dictum finibus egestas, orci nulla pulvinar sem, nec tempor libero lacus eu ante. Nam lacus erat, gravida eu placerat sit amet, facilisis sed urna. Suspendisse efficitur felis non ornare dictum. Nam sit amet nulla enim. Nulla eget scelerisque est. Suspendisse lacinia velit arcu, id lobortis felis facilisis in. + +Suspendisse potenti. Nulla porttitor metus ut nunc dictum tristique. Cras sit amet tortor eget ligula tristique efficitur. Ut at nisi id purus imperdiet laoreet. Sed sit amet malesuada urna. In nunc dolor, luctus ac condimentum ut, dapibus vel metus. Suspendisse pretium urna eget libero convallis vestibulum. Integer ut mauris hendrerit ex posuere euismod sed sed odio. Nulla egestas libero sit amet magna venenatis faucibus. Pellentesque semper vestibulum elit, et pretium felis scelerisque non. Suspendisse aliquet leo arcu, sed dapibus ex semper non. Donec lacinia dictum dignissim. Maecenas ipsum nunc, aliquet eget consectetur sit amet, aliquet vitae odio. + +Proin consectetur blandit feugiat. Nulla ac dictum quam. Vivamus suscipit scelerisque ipsum, vitae consequat neque sagittis id. Donec eu augue hendrerit, varius ipsum at, cursus massa. Morbi id augue id ipsum porttitor mollis. Phasellus nec libero eu arcu finibus dapibus. Nullam nec pharetra ante. Aenean sit amet urna eget justo tempus pharetra ut nec mi. Pellentesque viverra, ligula nec elementum ornare, ante elit eleifend enim, nec dignissim ligula elit in nibh. Vestibulum facilisis, felis eu condimentum dapibus, ante risus tincidunt urna, ut posuere dui augue ut ante. + +Nam arcu lacus, accumsan ac dolor in, egestas euismod est. Sed consectetur mauris et enim tincidunt semper. Donec sit amet pellentesque diam. Fusce viverra arcu a placerat fermentum. Nullam euismod dui eget egestas ultrices. Duis euismod viverra tortor eget eleifend. Pellentesque vitae neque dapibus, bibendum mi eu, auctor velit. + +Pellentesque congue consectetur turpis, eget auctor dui suscipit vel. Aliquam a sollicitudin turpis. Praesent nec blandit tortor. Suspendisse non nunc at urna tincidunt elementum. Integer eu elit nec urna maximus blandit quis at tortor. Nulla laoreet elit a purus tempus, et tincidunt lorem sagittis. Aenean semper erat eu neque hendrerit ornare. Cras posuere lorem nec orci vulputate finibus. Fusce tempor ex ac lacus gravida venenatis. + +Phasellus laoreet, libero vel fermentum euismod, sem diam accumsan quam, vitae gravida arcu est at sem. In bibendum, felis at viverra congue, felis nunc fringilla libero, ut scelerisque erat massa ut neque. Suspendisse potenti. Cras faucibus dolor vel tortor blandit, quis imperdiet magna semper. Nullam ut urna dapibus, vulputate est a, hendrerit purus. Vivamus vestibulum nisi a tempus placerat. Morbi vitae ultricies lacus. + +Nunc vel efficitur urna. Fusce bibendum suscipit mauris, quis imperdiet nisl bibendum non. Nam sed nisi imperdiet, pellentesque sem sed, pellentesque diam. Praesent luctus feugiat odio, eget fermentum lectus ullamcorper non. Phasellus pulvinar lectus sed ligula semper lobortis. Pellentesque fermentum ultricies fermentum. Quisque id turpis vel orci rutrum rhoncus id vel metus. Vivamus ex leo, accumsan eu luctus sit amet, tincidunt vitae erat. Sed eu mollis odio, ut ultricies lacus. Duis enim odio, pellentesque non velit vel, aliquam blandit erat. Mauris feugiat felis fermentum purus blandit, id rhoncus lorem tempus. Integer cursus, nunc vitae laoreet commodo, nunc lacus venenatis orci, quis eleifend risus est nec quam. Nulla tincidunt nunc ac purus tincidunt, eu molestie ipsum sollicitudin. + +Vestibulum ac varius velit, non suscipit nulla. Vestibulum a sagittis tortor. Suspendisse vestibulum felis quam, eget blandit nulla dictum vel. Praesent eu tellus ut lacus facilisis ultrices. Etiam fringilla nulla nec leo viverra faucibus. Sed nec sollicitudin nisl. Aenean libero massa, ornare sed elit ut, interdum fermentum arcu. + +Aliquam maximus diam et elit dapibus, eget condimentum sapien finibus. Etiam gravida nunc dapibus facilisis feugiat. Sed quis massa ligula. Nunc eleifend dolor lacus, at dapibus nisi vulputate eget. Etiam vel euismod urna, quis molestie nibh. Vestibulum bibendum erat non dictum sollicitudin. Suspendisse sed placerat lacus. Cras sollicitudin quam justo, a condimentum urna feugiat a. Quisque mauris libero, ultricies at ligula a, facilisis euismod ante. + +Morbi hendrerit maximus sapien ut auctor. Nullam nisi erat, aliquam ac metus eu, fermentum laoreet augue. Nam ultricies mi at vulputate laoreet. In ipsum est, blandit sit amet lorem id, egestas aliquam odio. Praesent venenatis lobortis mi. Aenean eu lacus consequat, mattis enim et, pellentesque leo. Duis convallis facilisis placerat. Donec porta, enim eget placerat congue, nisi augue faucibus odio, et fringilla purus elit vitae felis. + +Nulla et tellus a libero pellentesque eleifend vitae mattis nisi. Nunc placerat neque et sapien lobortis, aliquet pharetra nunc vulputate. Suspendisse potenti. Etiam ullamcorper lacinia varius. Suspendisse sit amet malesuada magna. Nam molestie mi nec diam tempus laoreet. Suspendisse urna tellus, scelerisque vitae purus et, dapibus fermentum felis. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec scelerisque eget neque sed hendrerit. Donec at ligula augue. Donec vulputate massa nunc, a feugiat nunc elementum ut. Donec pulvinar libero sit amet ante faucibus sagittis. Mauris quis diam ultrices, tristique sapien nec, tempus urna. Morbi hendrerit odio sit amet leo lacinia, non egestas diam condimentum. Suspendisse efficitur sapien eget elit euismod tristique. Duis posuere vestibulum risus id sodales. Ut eget mi in urna rutrum molestie non nec dolor. Curabitur sollicitudin pharetra lacus, in sodales tortor tempor vitae. Nullam rhoncus arcu vel euismod varius. + +Aenean malesuada, purus quis volutpat sollicitudin, lectus risus lacinia mi, a facilisis ante lacus a dolor. Aenean vel aliquam tortor. Vivamus ornare, tellus at malesuada suscipit, quam dolor egestas erat, id convallis enim augue a enim. Aenean ultricies sodales placerat. Vivamus vel erat ac mi vulputate commodo nec eget urna. Suspendisse potenti. Mauris quis dapibus est, id tristique libero. + +Suspendisse ex diam, imperdiet quis dui id, interdum sodales elit. Maecenas at nunc ligula. Etiam imperdiet convallis magna sit amet tristique. Proin hendrerit laoreet magna, eget malesuada elit rhoncus et. Curabitur eget odio imperdiet, molestie sapien pretium, efficitur turpis. Aliquam sed congue arcu. Pellentesque vitae mauris id neque pulvinar tempor. Donec lobortis tellus id gravida dictum. Nulla et varius ex. Vestibulum pharetra eu quam dapibus finibus. Nullam accumsan sagittis turpis. Mauris fermentum orci et tortor tempus, ac tristique odio sollicitudin. Duis nec elit et magna imperdiet molestie eget vel ante. + +Suspendisse tempus augue nec massa molestie aliquam. Pellentesque vestibulum, erat sit amet fringilla fermentum, sapien lorem tempor felis, at efficitur augue erat quis nisi. Proin nec felis sagittis, sollicitudin turpis eu, fringilla leo. Vestibulum at augue nec dui vestibulum aliquam a at metus. Duis ullamcorper eleifend bibendum. Maecenas viverra mauris lacus, et consequat nisl pharetra eu. Praesent rutrum diam eu mi aliquet, non pellentesque est suscipit. Vestibulum massa leo, blandit eget mi at, cursus faucibus nunc. Praesent nec bibendum libero, vitae cursus libero. Sed consequat vitae eros nec maximus. + +Pellentesque et tortor eget lectus feugiat venenatis. Integer ornare nisl quam, nec imperdiet justo finibus at. Morbi malesuada, ante sit amet rutrum tempor, risus lorem tristique urna, egestas varius purus ex ut sem. Etiam sit amet feugiat sapien. Etiam quis risus commodo, eleifend velit quis, tincidunt enim. Mauris fermentum lacinia velit quis efficitur. Nunc sit amet lacus sit amet urna viverra feugiat. Nullam sagittis rhoncus suscipit. Aliquam eu sem ligula. Sed sodales risus et tortor lacinia tristique. Nulla massa augue, malesuada sit amet euismod ac, euismod placerat nulla. Sed lobortis ex nec lacus facilisis, nec semper mauris ultrices. Quisque ornare non felis vitae pellentesque. Donec mattis ut magna sed imperdiet. Integer viverra tempus feugiat. + +Donec laoreet aliquam imperdiet. Fusce justo neque, dictum vitae fringilla vitae, euismod sed augue. Fusce sodales, lectus a congue bibendum, ante eros pharetra tortor, eu sollicitudin libero ipsum sit amet felis. Curabitur sed condimentum quam, in iaculis ante. Ut feugiat dictum odio, vitae hendrerit lacus volutpat sed. Sed scelerisque et metus nec gravida. Mauris mattis porttitor magna, ut maximus justo sagittis vitae. Donec at metus ut leo gravida vehicula in sed diam. Vestibulum dignissim suscipit sagittis. Nam varius et arcu sit amet lacinia. Sed eget elit rhoncus, elementum dolor a, vehicula orci. Nam vitae tellus sapien. Phasellus massa lorem, commodo quis placerat in, semper faucibus tortor. + +Vivamus id dolor mi. Curabitur sagittis posuere quam in mollis. Phasellus finibus ipsum vel elit tincidunt, a bibendum ligula vulputate. Suspendisse quis purus nisl. Integer mi sem, posuere quis nisi a, consequat consequat magna. Vestibulum bibendum mauris in risus auctor fringilla vel at lacus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus venenatis mauris at mattis eleifend. Donec tempus ipsum ac nisl convallis ultrices. + +Nullam eu nibh ut erat vehicula accumsan. Vivamus vitae faucibus libero. In in luctus odio. Nulla nec enim pharetra, fermentum erat in, tempor turpis. Sed ac magna suscipit, dignissim elit id, faucibus libero. Maecenas placerat in dolor et faucibus. Sed maximus purus dolor, non egestas massa rutrum non. Nunc ut rhoncus lacus. Sed sit amet dolor eu sapien sagittis molestie. Aenean ipsum erat, rutrum a diam et, varius porttitor ligula. Phasellus fermentum fermentum felis. + +Phasellus odio massa, lacinia ac hendrerit vestibulum, placerat sit amet erat. Praesent eget justo scelerisque, congue est in, pharetra mi. Etiam blandit turpis dui, a faucibus ipsum viverra vitae. Praesent sed scelerisque arcu. Cras nec venenatis ipsum. In et tempus metus. Pellentesque cursus quis nibh quis porttitor. Duis leo lacus, pretium eget rutrum eu, tincidunt lobortis tellus. Cras in erat tristique arcu faucibus luctus sit amet tempus erat. Nullam placerat, est a interdum iaculis, purus justo convallis arcu, at mattis erat sem tempor velit. Curabitur sapien sapien, tempor eget sodales vitae, blandit ac libero. Proin eget sem et arcu malesuada convallis non a est. + +Ut id sagittis urna. Morbi est mauris, molestie quis magna ut, convallis porta justo. Etiam in vulputate sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam consectetur enim nisl, sit amet pulvinar turpis elementum at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum convallis cursus libero vel feugiat. + +Curabitur vel scelerisque metus. Etiam aliquet faucibus quam, vel aliquet est mattis quis. Pellentesque pulvinar sodales augue, a dignissim sem tristique vitae. Pellentesque dolor quam, pharetra vitae rhoncus ut, blandit at elit. Sed dignissim diam turpis, ac condimentum justo iaculis vel. Donec facilisis eros sit amet est dapibus, vel pellentesque eros consequat. Integer euismod mollis metus, vel rutrum risus imperdiet ac. Fusce semper dolor sit amet mi congue accumsan. Maecenas sagittis magna justo, vel varius ante scelerisque id. Fusce at urna sapien. Aliquam dui sapien, facilisis eu magna laoreet, feugiat tincidunt elit. Fusce iaculis sit amet erat id dignissim. Suspendisse sollicitudin laoreet consequat. Pellentesque eu mollis quam, vel dapibus libero. Duis mattis tortor vitae orci vehicula, et viverra ipsum lobortis. Quisque iaculis sapien est, id sagittis tortor rutrum at. + +Duis ut condimentum risus, et venenatis nulla. Praesent urna ex, faucibus eu mollis nec, lobortis vitae neque. Vivamus a malesuada tellus. Quisque bibendum dolor nec massa porta, nec malesuada magna auctor. Phasellus non auctor turpis, et bibendum risus. Ut pellentesque justo non neque convallis, ut imperdiet diam sagittis. Nunc arcu nisi, rutrum sagittis neque elementum, finibus consequat felis. Proin euismod elit eu urna tristique ultrices. + +Curabitur id hendrerit ipsum. Aliquam tortor nisi, dignissim vel sodales a, ultricies a ipsum. Etiam commodo arcu sed volutpat varius. Proin vehicula lacinia tempor. Vestibulum feugiat nibh eu ante tempor efficitur. Etiam eleifend a orci eu euismod. Cras a tortor risus. Cras nec urna non urna malesuada maximus vel et ipsum. In ullamcorper porttitor dolor, at fringilla tortor tristique ac. Nulla gravida tortor nec dolor convallis, accumsan sollicitudin dui luctus. Vestibulum euismod vestibulum semper. Nam semper lacus dolor, vitae rhoncus nisi blandit nec. Phasellus turpis dolor, posuere sed sodales sit amet, interdum ut arcu. + +Sed blandit nulla et sem vulputate, et semper lacus posuere. Proin velit lorem, sagittis tincidunt aliquet vitae, fermentum sed orci. Ut interdum ultrices dolor eu tempor. Quisque pretium vulputate dui at blandit. Aenean pretium urna sed purus dapibus aliquam. Mauris tristique augue pretium magna scelerisque, vel cursus sapien porttitor. Nullam neque justo, aliquam non neque id, lobortis facilisis nibh. Duis molestie dui eu augue tristique porttitor. Sed posuere justo massa, efficitur vulputate erat posuere non. Cras quis condimentum tellus. + +Etiam eleifend ipsum ut justo viverra porttitor. Nam bibendum enim nec ligula vestibulum, vel fringilla velit posuere. Praesent leo enim, varius sit amet nulla ut, sodales sollicitudin nunc. Phasellus hendrerit varius ex quis tempus. Suspendisse maximus laoreet enim, ut bibendum tortor. Ut non magna vehicula augue condimentum scelerisque. Aenean efficitur elit vel rhoncus euismod. Vestibulum sit amet sollicitudin dui. Quisque ac diam nibh. Nullam quam enim, volutpat eu turpis et, posuere consectetur neque. + +Ut tincidunt semper dictum. Etiam varius enim sit amet metus consequat malesuada in at ligula. Cras nisl dui, tincidunt a urna et, porttitor rhoncus velit. Mauris interdum imperdiet lectus ac mollis. Aliquam sollicitudin ornare mi, a varius nulla faucibus aliquet. Nunc fermentum tempor ullamcorper. Pellentesque laoreet libero condimentum pellentesque pharetra. Nullam sed eros vel erat vestibulum dictum. Nulla nec interdum dui, quis finibus nunc. + +Sed ac turpis in mi ultricies finibus sit amet et diam. Pellentesque sagittis non ligula et convallis. Suspendisse interdum est aliquet erat rhoncus semper. Donec pretium turpis ante, vel posuere mauris facilisis vel. Vivamus nibh ligula, pharetra sit amet hendrerit nec, vulputate ut sapien. Nulla ullamcorper condimentum metus vitae imperdiet. Ut eget ex purus. Nulla dapibus malesuada pharetra. Praesent sit amet ornare turpis. Nulla pulvinar felis eget arcu mollis vulputate. Vestibulum eget semper felis. Donec viverra justo id mi tempor, a mollis ipsum porttitor. Maecenas aliquet volutpat ante eget tempor. Duis ipsum nisi, scelerisque quis metus vitae, blandit faucibus nisl. Mauris rutrum nisi sed lorem dictum ultricies. + +Nulla dictum arcu augue, aliquet ornare ligula suscipit ut. Integer libero erat, bibendum quis arcu at, consequat luctus sem. Ut ut erat tincidunt, porta erat efficitur, bibendum neque. Maecenas eget convallis sapien. Nullam facilisis ex et lorem fringilla, ut convallis leo dictum. Duis porttitor, ex eu venenatis faucibus, erat augue dapibus leo, sit amet scelerisque neque dui sed arcu. Praesent at diam nec sem sodales condimentum. Vivamus vehicula urna tellus, a semper ipsum cursus id. Duis auctor enim erat, laoreet volutpat dui rhoncus maximus. Suspendisse pellentesque euismod lacus, at auctor purus. Suspendisse volutpat imperdiet diam, id laoreet est egestas at. + +Fusce lobortis ex vel condimentum convallis. Vivamus fermentum convallis dolor quis rutrum. Donec tempus ornare maximus. Mauris quam neque, dignissim rutrum maximus sed, volutpat sed lectus. Donec id augue gravida, pretium purus eu, sagittis tellus. Maecenas tincidunt gravida felis nec pulvinar. Fusce turpis urna, sodales non rhoncus nec, sodales ac ipsum. Suspendisse risus felis, imperdiet id malesuada fermentum, ultricies et erat. Suspendisse potenti. Pellentesque tellus ipsum, dapibus eget pellentesque eleifend, venenatis sed risus. Ut sed mollis nisl. Aliquam lobortis aliquam ipsum, et ornare magna bibendum ut. Proin quis justo vitae neque tincidunt maximus ultricies et purus. Nullam non semper turpis. Quisque non mauris odio. + +Donec commodo tempus egestas. Vestibulum at diam vel mi tincidunt finibus. Donec aliquam magna id eros tristique finibus. Cras ut enim sapien. Proin gravida risus a nisi dapibus, ac vulputate nunc laoreet. Donec at leo vel nulla iaculis feugiat sed et ante. Nulla a arcu at ligula sollicitudin semper sed eget est. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Ut dolor magna, luctus id elit ut, interdum viverra lacus. Aenean sed tempor metus. Aenean arcu dui, eleifend quis est sed, luctus lacinia nulla. Morbi id luctus libero. Maecenas sodales leo eu sapien maximus porta. Praesent gravida augue eu ligula pretium cursus. + +Duis aliquam luctus tortor, eu facilisis quam sodales sed. Phasellus feugiat venenatis lorem, in scelerisque est efficitur quis. Cras quis nisl nisl. Quisque magna felis, tempus nec pharetra et, tempor id ligula. Pellentesque sed dignissim velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris convallis iaculis posuere. Nullam orci velit, venenatis eget enim quis, molestie consequat dolor. Nulla egestas risus vestibulum sagittis blandit. Nullam dui lacus, tempus euismod sapien a, eleifend sodales justo. In id tortor neque. Vivamus faucibus ante ac odio vestibulum, vitae condimentum nibh consectetur. In rutrum hendrerit est, sit amet mollis ex aliquet tempor. + +Donec non tincidunt sem. Nullam dignissim felis nibh, et accumsan felis tristique sed. Maecenas id massa erat. Ut justo ligula, aliquet eu condimentum a, aliquet eget ex. Phasellus et neque eu nisl consectetur ullamcorper. Sed gravida efficitur nisi, vitae posuere nisi tincidunt sed. Duis vitae molestie metus, vitae finibus urna. Integer at lacus vitae turpis convallis feugiat a dignissim libero. In dolor nibh, aliquam eu malesuada in, tincidunt vitae nisi. Nullam at lectus risus. Integer vitae ligula interdum, congue nibh id, tincidunt dolor. + +Mauris risus quam, mollis eu consequat vitae, molestie sit amet risus. Vestibulum congue vulputate nibh sit amet ullamcorper. Nullam elit justo, hendrerit euismod elit nec, gravida tincidunt ipsum. Aenean tellus tortor, commodo vitae cursus vitae, finibus quis mi. Nam in tellus scelerisque, cursus magna a, elementum tellus. Praesent ut lacinia justo. Donec consectetur, mi nec faucibus viverra, nisl justo suscipit est, in consequat ex urna vitae orci. Fusce molestie feugiat ex sed dictum. Phasellus interdum eros dapibus sodales volutpat. Morbi elementum sapien ante, quis sagittis justo fermentum at. Maecenas vestibulum massa quis eleifend rhoncus. Praesent auctor ex at urna pellentesque facilisis. + +Duis tincidunt porta quam, in commodo orci dapibus interdum. Donec mattis erat ante, nec faucibus magna pharetra id. Aliquam dignissim ultricies auctor. Duis quis ex mi. Phasellus ipsum mi, volutpat quis augue nec, dapibus aliquet lectus. Aenean id eros mi. Fusce rhoncus iaculis magna, commodo commodo nibh pellentesque ut. Donec consectetur lacinia erat ut luctus. Mauris efficitur erat vitae sapien fringilla sollicitudin. Maecenas a egestas ipsum. Aenean placerat congue augue, ut condimentum lorem accumsan in. Morbi ac quam nunc. + +Nunc posuere faucibus semper. Integer at convallis ex. Duis a purus molestie, dignissim mi quis, consequat nulla. Proin tempus congue ligula, sit amet ultricies enim consequat ut. Nunc ac est at nisl euismod cursus. Pellentesque vulputate molestie ipsum. Praesent varius, lacus non lobortis blandit, nibh lacus scelerisque nisi, sit amet interdum ligula tellus ac purus. Vestibulum sed quam tempor, pharetra tellus ullamcorper, tincidunt est. Nulla tempus tortor nec erat tempus eleifend pellentesque eget purus. Duis gravida dui justo, ac commodo ipsum mollis et. Nullam vitae nibh enim. Ut sodales mi eu diam sagittis, quis luctus tortor euismod. + +Maecenas a augue ac augue varius rhoncus. Fusce nunc turpis, mattis eu iaculis a, dapibus nec purus. Pellentesque imperdiet sit amet purus a blandit. Morbi augue tellus, venenatis nec tortor in, consectetur tincidunt nulla. Aenean purus libero, volutpat quis neque vitae, mattis efficitur eros. Donec sodales venenatis augue, sed faucibus magna accumsan convallis. Pellentesque efficitur elementum imperdiet. Cras at condimentum leo. Curabitur feugiat ut nisi facilisis commodo. + +Sed commodo sapien quam, quis vulputate orci lobortis nec. Aenean luctus dictum ipsum, non consequat sapien molestie vel. Proin blandit libero tortor, vitae efficitur tortor hendrerit at. Sed eleifend eu velit eu tempor. Nunc auctor tincidunt mollis. Ut molestie, erat ut lobortis convallis, odio felis sollicitudin elit, vitae ultricies lorem neque et dui. Sed venenatis tempus ullamcorper. Integer eget elementum nulla. Maecenas a placerat ipsum. Etiam sagittis sagittis rhoncus. Aliquam faucibus magna id lacus pharetra, nec interdum lacus sodales. + +Mauris ipsum sem, venenatis non elit in, feugiat pretium lacus. Fusce eget tincidunt dolor. Nam pharetra lacus vitae diam bibendum, ac placerat tortor aliquet. Sed eget gravida orci. Ut in orci lorem. Morbi condimentum ligula dolor, in dictum mi vestibulum sed. Suspendisse eu velit finibus, tincidunt dolor malesuada, mattis est. Morbi iaculis sapien vitae metus facilisis fringilla. Donec sed volutpat neque. Mauris ipsum metus, venenatis cursus mattis quis, convallis ultricies purus. Aliquam quam magna, sagittis at mi quis, lacinia iaculis turpis. Praesent viverra, ex nec euismod lacinia, urna risus pretium odio, sit amet mattis metus eros non lectus. + +Nam sed vehicula est, ac aliquet mi. Aenean iaculis placerat orci, ut lobortis dui vestibulum convallis. Vestibulum eleifend ante sed lorem interdum lobortis ut vel tortor. Sed auctor id nisl sed bibendum. Fusce maximus vulputate mauris, tempus sollicitudin nunc efficitur vitae. Nulla pellentesque molestie leo, quis hendrerit enim. In vel dapibus nisi. Nam at dui quis eros porttitor volutpat in id magna. Maecenas quis quam a justo cursus aliquet viverra sit amet mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut hendrerit est at augue pretium condimentum at ut velit. Suspendisse non gravida metus. + +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Praesent lacinia commodo elit, sed fringilla ipsum imperdiet ut. Proin a dictum ante. Suspendisse potenti. Praesent ac nulla volutpat, ultricies diam vel, auctor risus. Nullam aliquam elit vel quam suscipit molestie vel ac dolor. Ut pharetra finibus elit, id vulputate ex dignissim in. Ut ac finibus urna, a luctus magna. + +Fusce suscipit convallis eros nec blandit. Cras quis lectus nibh. Donec pulvinar rhoncus pulvinar. Vivamus nulla nisi, vestibulum vel neque et, dictum gravida ex. Vestibulum in arcu vitae nibh auctor rhoncus ac in massa. Sed semper enim ac dui sollicitudin porttitor. Etiam ante erat, laoreet sed dolor eget, cursus commodo lorem. Curabitur maximus tincidunt nulla at molestie. Phasellus ultrices felis a cursus facilisis. Nullam a semper tortor. Nulla ac vulputate justo. Mauris maximus nisi quam, elementum eleifend nulla condimentum nec. Aenean congue tincidunt mi, id mollis orci blandit vitae. Integer placerat orci at nisl vestibulum lacinia. + +Sed egestas posuere egestas. Quisque pulvinar velit commodo felis accumsan, a consequat purus laoreet. Ut at arcu at augue sodales rhoncus. Ut non nisl dui. Sed sed nulla quis orci eleifend auctor ut et sem. Aliquam erat volutpat. Donec egestas tincidunt leo in interdum. Donec varius odio nulla, id porttitor erat venenatis ut. Nulla ac nisl ut enim placerat congue. Fusce consequat purus eget risus luctus finibus. Donec in blandit odio. Sed vestibulum nisl ut diam vestibulum volutpat. Donec magna quam, tempor eget velit quis, blandit tristique lacus. Pellentesque et orci dui. + +Integer tempor mollis purus ut volutpat. Pellentesque efficitur cursus neque in ullamcorper. Integer ac tincidunt lacus, at venenatis tellus. Curabitur convallis commodo enim a convallis. Aenean sagittis sodales nibh, sed eleifend diam ultricies at. Vestibulum erat mauris, lobortis ac tempor a, viverra non sem. Nulla non ipsum mollis, dignissim elit a, laoreet enim. + +In laoreet purus eget orci rhoncus viverra. Quisque vel metus quis nulla hendrerit viverra. In consectetur velit vitae purus vestibulum, in hendrerit odio sollicitudin. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ut est sed ligula tincidunt fermentum nec ac nulla. Nam in sagittis velit. Vivamus vel dapibus erat, ullamcorper sollicitudin metus. + +Quisque pulvinar nulla eget mi mattis, ut convallis nulla ullamcorper. Vivamus placerat mauris vel elementum aliquet. Sed ac accumsan eros. Vivamus vitae rhoncus urna. Fusce gravida cursus varius. In posuere finibus leo cursus molestie. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris vulputate mi ut nunc finibus convallis. Praesent luctus erat eu urna facilisis convallis. + +Nam efficitur tempus augue varius porttitor. Pellentesque scelerisque dolor scelerisque, dignissim massa eget, iaculis nibh. Praesent viverra molestie dui a pulvinar. Mauris malesuada mattis felis, vitae sagittis metus pellentesque viverra. Phasellus faucibus congue blandit. Pellentesque mattis, justo sed imperdiet rutrum, metus metus porta nisi, vel malesuada lorem massa at dolor. Nullam nisi eros, tristique quis mollis ac, hendrerit nec leo. Praesent viverra molestie vestibulum. Nullam eu aliquam risus, at dignissim diam. Quisque blandit fermentum erat, sed ullamcorper augue pellentesque in. Integer eleifend libero non lacus sagittis auctor. Aliquam volutpat interdum accumsan. + +Etiam in eros porta, bibendum lacus eget, feugiat orci. Integer massa dui, commodo ac luctus nec, ornare id velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis dignissim scelerisque ex. Aliquam tempus cursus nibh, sed scelerisque libero sagittis ut. Morbi finibus vestibulum nulla, nec aliquam urna venenatis vulputate. Pellentesque ultricies, lorem a dignissim bibendum, augue nisi eleifend libero, cursus ultrices nulla purus a nunc. Quisque dictum pulvinar turpis vitae finibus. Etiam eget ultrices diam. Sed commodo enim ante, fermentum rhoncus tortor tincidunt ac. Suspendisse fermentum aliquet erat non posuere. Aenean vel dapibus lorem. + +Aenean lorem libero, rutrum vel egestas vel, pellentesque ut ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum felis diam, pellentesque sit amet ultricies sed, vestibulum in leo. Morbi aliquet, quam ut fringilla sollicitudin, justo risus suscipit mi, in vulputate neque erat ut turpis. Duis auctor dui non urna sagittis dictum. Vestibulum nec felis vel sapien congue volutpat a a orci. Phasellus tincidunt libero ac lorem feugiat, ac elementum lectus eleifend. Praesent sit amet est id massa convallis congue. Integer ac nunc eget orci pharetra vehicula. Mauris et ultricies diam. Nunc et pellentesque lacus, eget bibendum sapien. Morbi condimentum mi ac metus euismod convallis. Proin consequat rhoncus varius. Maecenas feugiat elementum vulputate. + +Vestibulum tempor feugiat dapibus. Ut ornare in augue non euismod. Nulla faucibus gravida condimentum. Curabitur pharetra dui non lorem sagittis iaculis. Quisque vitae nibh magna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce quis pharetra sem, sed aliquet lectus. + +Fusce volutpat tempor tincidunt. Pellentesque ultricies imperdiet tincidunt. Integer porta dictum lorem, non luctus tellus bibendum sit amet. Quisque posuere felis et est dapibus, commodo rutrum leo molestie. Fusce sodales felis sed sem pharetra pretium. Praesent nec sollicitudin nisl. Donec volutpat convallis elementum. Duis id libero augue. Nulla facilisi. + +Nulla viverra, urna nec efficitur vulputate, metus odio lacinia purus, in sagittis elit erat vel massa. Fusce ligula leo, finibus non felis dignissim, dictum ullamcorper odio. Phasellus vel eros eu sem venenatis sagittis a nec nulla. Pellentesque sapien elit, lobortis quis dictum et, maximus vitae metus. Curabitur quis aliquam eros. Quisque cursus condimentum urna vel efficitur. Etiam aliquet lorem ut varius suscipit. In viverra molestie felis vitae vehicula. Curabitur cursus, nunc sodales faucibus ullamcorper, urna magna dapibus velit, accumsan blandit mi quam nec justo. Donec ac dui lorem. Sed eu sagittis nulla. Suspendisse ut ante lacus. Fusce non tristique felis. Nulla convallis consectetur arcu, semper egestas urna efficitur quis. Nulla ac turpis at leo pretium maximus. Morbi cursus diam ac purus imperdiet, non commodo tellus cursus. + +Mauris ut eros suscipit, suscipit nisi vel, porttitor sapien. Morbi in nulla sapien. Cras maximus pretium justo, vel eleifend urna vulputate sed. Aenean egestas nisl ex. Curabitur sed pharetra ligula. Nulla blandit lorem id mauris tincidunt, at elementum risus aliquet. Quisque id interdum dui. + +Aenean nec elit condimentum ex viverra hendrerit. Pellentesque blandit nibh elit, a ultrices orci vestibulum vitae. Donec ultricies malesuada justo ac luctus. Pellentesque laoreet nunc a sem varius, ac dapibus ligula volutpat. Aenean sem felis, aliquam nec ullamcorper quis, ullamcorper non ante. Pellentesque libero leo, sagittis nec tempus a, tincidunt eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec elementum massa vel diam suscipit suscipit. Suspendisse eget neque porta, cursus orci eget, imperdiet libero. + +Vivamus faucibus vulputate sapien, non pulvinar ex sodales vitae. Maecenas consequat est urna, id faucibus eros dictum at. Vestibulum tortor mi, porta vitae sagittis sed, convallis quis enim. Vestibulum gravida justo pulvinar sem ornare, efficitur dictum neque varius. Nullam egestas congue tellus vitae interdum. Nam lectus erat, porta vitae lorem non, mollis semper velit. Nulla vestibulum tincidunt elit. Phasellus sed vulputate leo. + +Sed efficitur quam ac iaculis volutpat. Praesent nec feugiat ex. Aenean at ligula iaculis, imperdiet lacus et, condimentum magna. Integer euismod leo id mauris condimentum, quis semper lacus blandit. Sed volutpat neque urna, sit amet ullamcorper est dignissim non. Vestibulum tristique sollicitudin risus, sed hendrerit massa finibus id. Vestibulum sit amet risus non eros vehicula malesuada. Nam facilisis lacus sed quam pulvinar, at fermentum lectus tincidunt. + +Vivamus laoreet sem nec odio blandit, ut ornare sem egestas. Suspendisse potenti. Nulla aliquam pretium volutpat. Maecenas a orci suscipit, aliquam eros a, maximus urna. Aliquam eu gravida justo, et hendrerit justo. Suspendisse a commodo lorem, quis viverra nisl. Nulla vel pellentesque nisl. Nulla ligula tellus, vehicula sit amet turpis in, sodales tincidunt tellus. Proin ut nibh sed ipsum porta dignissim vel sed mauris. Interdum et malesuada fames ac ante ipsum primis in faucibus. In hac habitasse platea dictumst. Vestibulum efficitur nulla rutrum, accumsan lacus at, dignissim tortor. Morbi egestas metus ut nibh tincidunt posuere at sed elit. Integer eget hendrerit nulla. + +Donec sagittis sagittis tempus. Proin iaculis neque vehicula commodo faucibus. Pellentesque erat ante, vehicula sed efficitur vitae, varius non sem. Mauris pellentesque, felis nec egestas scelerisque, nulla nunc fringilla arcu, feugiat fringilla quam neque in elit. Nullam ut ex odio. Mauris enim risus, consequat at suscipit at, pulvinar ut arcu. Fusce mollis sem sed tellus tempus pellentesque. In pharetra, libero sed tristique vestibulum, massa velit egestas risus, at mollis augue lorem sit amet turpis. Mauris risus dui, sagittis vitae congue ut, condimentum ut augue. Quisque non sollicitudin purus, sit amet consectetur sapien. + +Sed scelerisque ipsum sed augue varius, sit amet ultricies purus posuere. Etiam vehicula at nunc in posuere. Cras eget risus a sapien mollis facilisis. Morbi vel purus auctor, faucibus mauris non, malesuada leo. Donec venenatis consectetur libero in pretium. Ut efficitur molestie metus id scelerisque. Nunc dolor justo, pharetra vel sagittis vitae, placerat ut justo. Donec tempor fermentum est semper auctor. Nullam tincidunt risus non risus elementum varius. Suspendisse euismod augue metus, nec pellentesque enim sagittis ac. Morbi et lectus quis justo commodo varius commodo vel risus. In bibendum purus in erat ullamcorper, sit amet dignissim sapien scelerisque. Quisque tempus elementum rutrum. + +In viverra sollicitudin pretium. Sed finibus scelerisque sollicitudin. Nulla lobortis purus in lectus iaculis, a ornare ex accumsan. Morbi malesuada mollis placerat. In hac habitasse platea dictumst. Pellentesque sed dui quis odio scelerisque molestie eu nec libero. Proin viverra magna ligula, at luctus lacus posuere sed. Nullam non congue mi. Praesent non mattis neque, sit amet tempus diam. Mauris eget cursus lacus, id dictum mi. + +Sed semper velit in vehicula tristique. In lobortis est augue, et maximus tellus iaculis ut. Proin quis ex maximus erat iaculis imperdiet. Curabitur ultrices turpis ac nibh congue ornare. Fusce a aliquet felis, nec sodales tellus. Nunc mauris ante, feugiat et facilisis at, pharetra ultricies dui. Integer felis metus, porttitor ac ipsum vitae, placerat varius ex. Morbi in dui a nunc aliquam posuere. Nulla tempus tortor ac lorem consectetur sodales. Praesent sit amet placerat diam. Curabitur sodales mauris ante, et tincidunt lacus ultricies quis. Nulla eu finibus nisl, sit amet volutpat leo. Donec ligula enim, feugiat non aliquet nec, congue interdum lorem. Pellentesque a nisi blandit, semper massa sit amet, auctor eros. + +Aenean ligula libero, commodo a erat at, tristique facilisis quam. Sed congue fringilla lacus, vel iaculis quam suscipit ornare. Curabitur non pretium mi. Donec condimentum odio dui, id malesuada ipsum porta ac. Aliquam imperdiet cursus ullamcorper. Duis cursus tincidunt nibh sit amet cursus. Aliquam urna orci, sodales ac ipsum at, placerat lobortis neque. Sed sit amet convallis felis, vestibulum finibus arcu. Phasellus venenatis egestas felis, id aliquam turpis iaculis non. Proin a lacus ut felis malesuada sodales. Duis elementum, eros ac placerat bibendum, quam nisl varius mauris, vel venenatis neque augue et justo. Nunc varius sem velit, vel tempus mi aliquet id. Fusce purus massa, mollis id nulla non, dignissim cursus massa. Sed sollicitudin interdum felis, nec eleifend nisl feugiat eget. Nulla rutrum id odio ut consectetur. Maecenas fermentum turpis vitae libero ullamcorper suscipit. + +Vestibulum auctor lectus ligula, non sollicitudin odio maximus nec. Cras faucibus, felis sed suscipit tempor, risus lorem consectetur est, eget pulvinar nibh dui eu dolor. Vestibulum pellentesque, ex aliquam vulputate laoreet, libero lorem rhoncus risus, vitae congue velit justo vitae nisi. Nullam placerat venenatis tortor, sit amet lacinia augue placerat nec. Quisque vulputate lectus vel pulvinar condimentum. Nullam at libero iaculis, mattis purus vel, tincidunt diam. Suspendisse eu ullamcorper eros. Nulla id eros odio. Ut enim leo, ultricies quis mauris sed, interdum commodo felis. Etiam enim lacus, scelerisque a interdum eget, mollis vel tellus. Phasellus vel vulputate orci. Nulla ornare leo sed arcu rhoncus convallis. Duis libero arcu, pulvinar ut tellus vitae, aliquam euismod magna. Donec semper nisi eget nulla tempus, sed condimentum massa sollicitudin. + +Suspendisse bibendum ante vitae ullamcorper semper. Praesent dui est, elementum nec lacus in, pellentesque accumsan sapien. Cras quis urna at lacus posuere commodo eget nec arcu. Nunc varius ante quis ligula rhoncus, ut dignissim metus commodo. Integer volutpat gravida nunc eget faucibus. Quisque imperdiet, mauris vitae cursus malesuada, lacus mauris tempus sem, ut accumsan purus lectus quis nisi. Vestibulum aliquam dui sed nisl laoreet, id posuere quam imperdiet. Aliquam ultricies non enim vel tempor. Donec sed feugiat justo, vel rutrum enim. Phasellus lorem quam, dapibus a tincidunt vulputate, pellentesque non lorem. + +Nunc quam diam, condimentum sit amet enim semper, hendrerit laoreet magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam et pulvinar leo, ac fermentum lectus. Sed erat ante, mattis ac tempor vitae, euismod at tortor. Ut sagittis fringilla scelerisque. Ut pulvinar molestie leo eu faucibus. Sed quis efficitur purus. Pellentesque nibh nisi, porta id orci id, sagittis sodales tellus. Ut venenatis velit a lectus lacinia, at ultrices nisi commodo. Vivamus eros orci, ornare vel ullamcorper elementum, posuere id ligula. Aliquam non massa pellentesque, semper ipsum scelerisque, dapibus leo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla pretium leo diam, sit amet bibendum lacus tempus quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer quis pulvinar nibh. + +Mauris sed eros nisi. Cras malesuada, turpis vitae laoreet porta, sapien odio maximus nulla, ut efficitur mauris odio tincidunt elit. Nam ut urna eros. Sed at vestibulum risus. Nulla luctus pulvinar rhoncus. Pellentesque maximus ligula a est ullamcorper, sed tempus tortor ultrices. Curabitur ligula odio, mollis sit amet risus quis, tempor auctor magna. Suspendisse vel quam quis lorem commodo facilisis. Donec eu ipsum suscipit, molestie dui sed, fringilla dui. Proin placerat ex a lorem sodales convallis. Sed molestie quam mi. + +Fusce laoreet nec dolor id bibendum. Nunc condimentum vulputate massa lacinia fermentum. Donec maximus cursus eros sed porta. Nunc eu porta turpis. Nulla cursus et nisl eu elementum. Ut rutrum scelerisque aliquet. In tellus erat, rhoncus id tempus vitae, vestibulum non quam. + +Vivamus luctus elit a efficitur dapibus. Praesent magna mi, pellentesque sed accumsan dapibus, blandit at lectus. Praesent sodales erat diam. Pellentesque placerat lobortis enim, a dapibus ligula molestie ut. Phasellus dui augue, suscipit ac urna non, iaculis eleifend augue. Maecenas sed elit iaculis, pretium ligula lacinia, aliquam libero. Nulla sem mi, pellentesque viverra lorem vel, luctus vulputate augue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque fermentum nunc ex, at lobortis turpis congue vitae. Suspendisse iaculis elementum enim commodo facilisis. Vestibulum non neque tellus. Proin vitae sodales urna. Mauris interdum purus et neque tempus, vitae mattis libero finibus. Suspendisse iaculis condimentum nibh, eu mattis enim vehicula a. + +Fusce dictum urna non lectus ullamcorper vestibulum id in elit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc fermentum odio non ante sollicitudin posuere. Praesent vulputate quam nec magna euismod pellentesque. Etiam tempor nunc eget velit volutpat eleifend. Mauris sodales nunc ullamcorper, gravida purus eget, rutrum sem. Nam a sapien rhoncus, scelerisque lacus ac, condimentum ipsum. Pellentesque suscipit, ligula ut rhoncus ullamcorper, ligula augue rutrum lorem, eget aliquam risus tortor eget metus. Curabitur scelerisque bibendum purus vel vulputate. Morbi et odio at neque sodales suscipit. Nam at pretium nulla. In scelerisque vulputate suscipit. Nulla facilisi. Pellentesque eu sem eu ligula efficitur viverra. Pellentesque gravida consequat urna id bibendum. + +Nulla consectetur facilisis est nec aliquam. Proin hendrerit magna suscipit ante accumsan hendrerit. Nulla ut sem id neque tempor vehicula. Sed eget massa eget sem placerat tincidunt. Ut eleifend, justo sit amet egestas lacinia, ex velit pharetra nulla, nec aliquet elit neque ut turpis. Quisque in gravida velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed euismod malesuada convallis. Ut ultricies, magna et blandit fringilla, dolor dolor maximus sapien, in aliquet augue est vel odio. Nulla commodo elit massa, euismod tempor turpis aliquet eget. Morbi non odio et tortor egestas ultrices. Etiam semper, ipsum et dictum pharetra, eros purus bibendum lacus, vel laoreet lacus sem vehicula nibh. Nunc enim nulla, pulvinar sit amet lobortis sed, porttitor molestie risus. Morbi tincidunt diam mi, nec auctor sem rutrum at. + +Donec pellentesque odio odio, eu condimentum risus consectetur quis. Vivamus ullamcorper, mauris non semper rutrum, dui risus suscipit tellus, ut tempor velit risus vitae nibh. Duis tempor nibh at tristique rutrum. Cras congue nisl at sem sollicitudin efficitur. Aenean auctor purus vel libero fermentum elementum. Mauris convallis orci id interdum accumsan. Aliquam at metus risus. Sed accumsan, quam id dignissim congue, velit risus eleifend ligula, ut fringilla elit erat at neque. Aliquam tempor, quam ut ultrices volutpat, orci lectus vulputate turpis, eget pharetra purus nisi non lorem. Ut condimentum convallis justo, a volutpat eros. Sed tempus turpis leo, in convallis est fringilla sed. Vivamus eu laoreet tellus. Quisque ullamcorper ullamcorper leo. Nam fermentum egestas facilisis. Nulla egestas ligula feugiat urna molestie, faucibus convallis erat accumsan. Cras pellentesque ipsum lectus, vitae luctus dolor suscipit nec. + +Vivamus vel nibh sed mi tincidunt cursus quis facilisis tellus. Donec eget est eu velit pretium molestie. Maecenas lacinia risus turpis. Sed tristique id risus sit amet venenatis. Duis maximus, metus ac molestie convallis, quam ex dapibus sem, at rutrum metus nisi quis lectus. Suspendisse sodales in nisi at hendrerit. Maecenas suscipit lobortis vulputate. Nunc convallis sit amet elit eget porta. Mauris tincidunt massa in augue finibus iaculis. Vestibulum imperdiet, orci vel bibendum laoreet, ligula quam mollis lacus, a eleifend nisi tellus vitae ipsum. Cras non ante sollicitudin, auctor dolor id, condimentum tellus. Morbi malesuada leo nec lectus scelerisque, nec interdum lacus ornare. Integer ultrices ligula nunc, sed blandit urna aliquam eget. Proin consequat viverra ex non rhoncus. Phasellus id nibh at tortor sodales blandit. Donec dignissim ipsum vel ligula malesuada rhoncus. + +Nullam mauris sem, dapibus ac nisl at, hendrerit faucibus nisi. Mauris dictum fermentum pellentesque. Praesent in gravida odio. Vestibulum pharetra iaculis est quis tincidunt. Sed venenatis rhoncus lacus, non porta ante vulputate at. Duis fringilla ipsum at urna euismod molestie. Duis a porttitor ante. Mauris pharetra elit et metus auctor, a eleifend sapien porta. Vivamus ut tincidunt purus, lobortis euismod tellus. Nullam non nulla gravida, laoreet tellus ac, placerat urna. + +Sed justo sapien, scelerisque nec nisl vel, efficitur aliquet purus. Phasellus eget posuere nisl. Integer porta vel purus nec ornare. Pellentesque rutrum in nulla at hendrerit. Nullam elementum sodales volutpat. Duis tempus, purus sagittis auctor ullamcorper, dui erat hendrerit nulla, id finibus eros dui quis risus. Quisque posuere nunc quis augue placerat lacinia. Nunc quis lorem vulputate, tincidunt enim nec, rhoncus odio. Ut porttitor porta velit ut vulputate. Duis convallis sagittis magna nec gravida. Ut eu luctus sem, non placerat erat. Vivamus viverra ut ligula ac finibus. Cras ligula urna, tincidunt id risus eu, dapibus viverra lorem. Aliquam erat volutpat. Sed dolor nisi, faucibus non ligula faucibus, laoreet bibendum diam. Vivamus sagittis lacus ut condimentum tincidunt. + +Proin tristique mi ullamcorper dolor accumsan mollis. Nulla facilisi. Pellentesque iaculis mattis augue ac rutrum. Mauris in nulla at ligula fringilla pulvinar. Nam commodo ornare nibh ac viverra. Maecenas eget augue a risus mattis ullamcorper non at nibh. Aenean nec erat diam. Pellentesque augue nisl, venenatis mollis dolor non, bibendum malesuada leo. Pellentesque elementum purus ornare mauris iaculis porttitor. Sed mollis tempor dui eu elementum. Donec porttitor tellus non mattis gravida. Mauris venenatis suscipit convallis. Sed dolor sapien, pellentesque et tellus a, tempus cursus magna. Nunc lobortis leo magna, at maximus orci ultrices eget. Nullam vulputate dictum nisi, vel egestas orci. + +Nam nibh mi, ornare sit amet nibh vel, lobortis lacinia leo. Ut egestas mi vel nunc luctus vestibulum. Nullam id tempus felis, eget convallis erat. Aliquam venenatis ut tellus id fringilla. Aliquam erat volutpat. Sed vehicula, odio nec mollis dapibus, ligula sapien consequat risus, ut volutpat diam neque ut neque. Mauris venenatis libero vitae sem elementum, sit amet maximus massa varius. Maecenas malesuada mi id leo euismod, eu maximus sapien vehicula. Nulla facilisi. Duis nec venenatis ante, non pulvinar lacus. In sollicitudin sit amet velit suscipit egestas. Maecenas a lectus euismod, dictum nulla at, malesuada lectus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec lorem massa, laoreet non elit nec, sollicitudin blandit felis. + +Cras dignissim fermentum tellus ut fringilla. Maecenas maximus eros pretium sagittis venenatis. Maecenas id enim ac est semper efficitur ac hendrerit leo. Cras eu arcu tincidunt risus pellentesque aliquam. Pellentesque vel sodales libero, non rhoncus enim. Integer vulputate vulputate libero, eu consectetur eros euismod vitae. Fusce magna nibh, vehicula sed turpis eget, malesuada ultricies sem. Sed convallis sem nisl, rhoncus mollis mauris bibendum ut. Pellentesque vitae massa a justo dapibus laoreet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed mollis egestas nisl vel ultricies. Phasellus sit amet varius leo, sed vehicula sapien. Etiam in urna eget neque aliquet ultricies in nec quam. Aliquam at feugiat elit. + +Maecenas placerat nisi ultrices neque pulvinar, et ultrices ante tempus. Vestibulum laoreet quam lacus, eget commodo magna dictum consectetur. Aliquam erat volutpat. Aenean tincidunt ipsum sit amet justo aliquet tempus. Quisque blandit sit amet est facilisis dictum. Sed non consequat dui. Integer purus diam, gravida quis tortor lobortis, iaculis vehicula sem. Morbi pellentesque est elit, vitae interdum elit laoreet et. Vestibulum in blandit ante, eu blandit velit. Morbi sagittis eu est eu vulputate. Nullam ac mi feugiat nibh feugiat suscipit. Sed interdum tincidunt efficitur. Integer dictum sem erat, ut pharetra libero lacinia in. Mauris at hendrerit odio, a facilisis lacus. Donec blandit massa ac nisi ultrices faucibus. + +Aenean tempus id ligula at mollis. Cras id dui magna. Integer id neque tincidunt, rhoncus nunc et, laoreet nibh. Pellentesque consectetur odio ligula, et consectetur tortor scelerisque non. Cras quis ex pharetra mi ornare lobortis. Phasellus fringilla interdum felis, vel aliquam ligula. Mauris cursus varius turpis in iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent eget mi id nulla semper dapibus. + +Vivamus lacus augue, eleifend dignissim ipsum eu, interdum accumsan massa. Nam id quam vel ligula consectetur volutpat id eget eros. Aliquam sed felis velit. Duis egestas velit ac dolor blandit, ut elementum mi tincidunt. Integer varius nisl a sapien pellentesque, et pellentesque ante elementum. Fusce ac orci interdum, faucibus nisl ut, sagittis nisi. Vestibulum sed diam eu magna congue ornare. Integer dignissim ligula sit amet mauris pretium vulputate. Duis ac tincidunt magna. Sed vehicula, ante nec vulputate venenatis, mauris odio blandit dolor, vitae lacinia nibh lorem et metus. + +Sed non sapien vel lorem tempor semper ac ac tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas auctor ante sit amet suscipit vulputate. In ac cursus turpis. Donec eu sem bibendum, blandit est nec, blandit tortor. Pellentesque scelerisque justo vitae magna cursus, vitae egestas libero interdum. Pellentesque vitae ligula vel mauris vehicula convallis. Nunc ornare lectus sit amet lorem aliquet dignissim. Cras nec ligula pretium, semper nulla a, pulvinar lacus. Sed ac augue bibendum, posuere ipsum eu, venenatis quam. Sed elementum nunc quis odio pellentesque, a vestibulum ex congue. Cras id malesuada arcu. Mauris ut egestas nulla, sed tempus ligula. Donec ac elit ut nisi pulvinar elementum. Suspendisse id auctor lectus, vitae ultricies lacus. + +Vestibulum pulvinar ornare cursus. Curabitur accumsan sollicitudin mi in vestibulum. Vestibulum vulputate tincidunt luctus. Suspendisse potenti. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus a mattis neque, id malesuada odio. Aliquam id auctor magna. Vestibulum quis nibh lacinia, tincidunt felis sed, vestibulum ex. + +Donec placerat ipsum diam, vel imperdiet velit eleifend ac. Quisque dapibus erat non dui convallis eleifend. Praesent pellentesque felis id suscipit rutrum. Donec quis lacinia turpis. Nulla justo eros, fringilla vel fringilla in, euismod sollicitudin arcu. In ut lobortis arcu, at rhoncus elit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Pellentesque iaculis quam nec interdum eleifend. Fusce mauris mauris, imperdiet sollicitudin volutpat eu, sollicitudin blandit leo. Vestibulum hendrerit diam gravida erat imperdiet, blandit dignissim dui tempor. Phasellus viverra ante quis aliquam finibus. Duis sed condimentum augue, nec sollicitudin dui. Ut ullamcorper metus ac ligula accumsan, ac fringilla metus gravida. Morbi rhoncus est nunc, at lacinia ex fringilla sit amet. Integer lobortis ultricies nisi vitae accumsan. Nulla nec sagittis ante. Mauris bibendum nisi ut magna vulputate maximus. Praesent in elit eu metus dignissim cursus. Ut a tellus felis. Mauris eget ornare diam. + +Suspendisse potenti. Nam nec tortor ex. Duis eu elementum ligula. Pellentesque efficitur sodales orci, vitae sollicitudin odio tempor ac. Nam lacus ante, lobortis vitae lobortis eu, eleifend et velit. Nulla et lectus eu nibh tristique malesuada a sit amet felis. Proin molestie, lectus vitae sagittis malesuada, massa velit finibus est, id vestibulum dolor felis eget lectus. Phasellus varius pellentesque neque, a malesuada ex mattis eget. Nulla facilisi. Phasellus interdum placerat lobortis. Sed et odio tristique, viverra libero et, sagittis diam. Proin tristique lectus id bibendum maximus. Integer ornare euismod ligula nec malesuada. Nulla facilisi. Nullam bibendum hendrerit pharetra. + +Duis pharetra auctor felis, eget aliquet justo commodo at. Donec quis nibh non arcu sodales efficitur. Aenean lorem sapien, tincidunt eu sapien nec, convallis tempor diam. Curabitur volutpat, felis ut congue euismod, lacus nisl euismod ipsum, vel consectetur orci nibh sed ligula. Curabitur consectetur vitae mauris sed tempor. Fusce vel pretium nibh, et tristique dolor. Aliquam semper a ante non finibus. Praesent euismod urna augue, at feugiat orci commodo ut. Duis imperdiet purus non augue cursus gravida. Maecenas sodales purus et sollicitudin venenatis. Nam ultrices lorem lectus, ut pretium turpis hendrerit eget. Vestibulum vel lacinia lectus, at dignissim diam. Vivamus ut tortor ac tortor blandit finibus. Etiam porttitor tortor sit amet elit lacinia gravida. Pellentesque pretium, orci vitae tempor vehicula, nisi tellus tincidunt sapien, id egestas felis quam a nunc. Pellentesque sed vestibulum nisl. + +Mauris congue, justo vel dapibus pretium, ipsum augue consectetur leo, at aliquam ipsum neque non mauris. Nam sollicitudin in urna eu blandit. Aliquam erat volutpat. Morbi eget interdum ante. Pellentesque congue gravida arcu, eget ornare ante elementum nec. Integer a auctor dolor, in varius sem. Etiam dictum nibh magna, at volutpat nunc blandit eu. Curabitur at sem ipsum. + +Nulla porttitor erat eget volutpat iaculis. Pellentesque egestas, nisl vel ultrices efficitur, ex magna condimentum urna, in tincidunt massa turpis eget metus. Etiam vitae magna elementum, fermentum justo ut, pretium velit. Aliquam aliquet venenatis malesuada. Quisque consequat enim metus, pellentesque blandit leo rhoncus id. Vivamus vestibulum, est in condimentum mollis, ligula eros blandit lorem, vitae dignissim lectus mi eget nulla. Cras posuere leo at neque convallis, sed pretium massa ultrices. Morbi tempus porttitor turpis. Nam vitae mauris et massa venenatis tincidunt. Quisque vestibulum lacus ligula, in sodales felis elementum vel. Sed a tellus condimentum, sodales nisi id, aliquet elit. + +Vestibulum ac ex eu turpis lacinia condimentum nec eget ipsum. Nulla non imperdiet erat, at malesuada lorem. Praesent efficitur imperdiet augue vel laoreet. Sed dignissim, ante non porta feugiat, enim nunc rhoncus lorem, eu luctus turpis risus sit amet orci. Vivamus bibendum pharetra venenatis. In at gravida nisi. Vestibulum pulvinar sapien ac augue scelerisque, vitae blandit nibh malesuada. Nunc vitae est faucibus, accumsan risus a, laoreet urna. Cras imperdiet lacus in augue facilisis dignissim. Duis sed nisi quis diam mollis rutrum. + +Vestibulum eget egestas neque. Praesent viverra, velit quis porttitor euismod, sem libero venenatis mauris, id congue urna nulla sed lorem. Nulla mollis metus nec diam malesuada aliquam. Duis ac risus nunc. Cras sollicitudin urna nunc, id sodales quam gravida sit amet. Fusce in vulputate orci, in venenatis lorem. Donec a sagittis ipsum. Quisque consequat sapien tellus, sed efficitur lacus aliquam eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean in neque at augue elementum commodo pellentesque eget ligula. Nullam condimentum efficitur tincidunt. Phasellus posuere tincidunt odio sed facilisis. Aenean eu risus at est euismod facilisis. Curabitur elit purus, malesuada quis blandit id, rutrum vitae dui. Praesent porta rutrum erat, ullamcorper viverra nunc. Cras ut velit dui. + +Etiam posuere pulvinar mi at ullamcorper. Pellentesque finibus, tellus non convallis commodo, orci nibh dapibus nisl, at aliquam purus nulla eget dui. Praesent fringilla urna nec nulla pellentesque, nec rhoncus turpis ultricies. Sed laoreet velit pellentesque libero varius, ac interdum urna viverra. Phasellus sed consectetur massa. Morbi quis velit nec ipsum varius tempor. Proin id sodales felis. Aliquam lacinia risus quis ligula condimentum sodales. Nulla vel arcu aliquet neque iaculis aliquet. Cras sed lorem eu turpis tincidunt sodales. Sed pulvinar elementum ligula, nec faucibus nisl. Fusce nec tellus eget dui tempor sagittis. In vitae enim in ex viverra commodo. Duis est erat, fringilla ac semper a, dapibus in tortor. + +Maecenas commodo vulputate iaculis. Aliquam non facilisis est. Donec pellentesque vitae nibh nec volutpat. In commodo metus placerat lorem commodo, non lacinia nibh bibendum. In viverra rhoncus erat. Mauris nec nisl blandit, elementum justo nec, accumsan libero. Aliquam elementum, velit et ullamcorper convallis, turpis lorem elementum lorem, quis consequat tortor eros ut erat. Curabitur et enim quis felis vulputate congue et vel purus. Sed elementum interdum ipsum, sed tincidunt arcu scelerisque et. + +Aenean interdum elementum mauris ut porta. Mauris vel purus ac odio vulputate pulvinar at quis odio. In turpis turpis, convallis in augue id, elementum vulputate lorem. Sed tincidunt fermentum vulputate. Nunc ipsum ipsum, molestie vel convallis id, pharetra vel arcu. Maecenas vel dui elit. Sed blandit dolor sit amet risus commodo faucibus. Duis rhoncus felis arcu, vel aliquam nisi faucibus sed. Suspendisse cursus eget nunc ut bibendum. Fusce non ligula risus. Curabitur vitae cursus metus, quis fringilla diam. Phasellus turpis ante, pulvinar ac turpis tristique, sollicitudin congue lectus. Proin quis ipsum at ipsum euismod euismod. + +Nunc ut fermentum nunc. Donec id commodo lacus, at hendrerit justo. Donec cursus purus sodales nunc commodo, quis bibendum elit hendrerit. Quisque quis pellentesque nibh, ac vulputate neque. Aenean non placerat felis, eget feugiat ligula. Vivamus a eros accumsan, cursus neque at, ultricies magna. Fusce tincidunt tellus vitae mi rutrum laoreet sed quis ligula. Nullam ullamcorper ligula ligula, vel fringilla metus aliquet a. Morbi aliquet, mauris vel interdum venenatis, ex arcu venenatis tortor, at tincidunt dui ipsum et arcu. Nulla blandit gravida nulla ac iaculis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ullamcorper lectus in sapien lobortis, eget posuere massa varius. + +Cras lacinia nunc faucibus mauris placerat pretium eget non sapien. Morbi viverra bibendum posuere. Aliquam accumsan sagittis dolor non iaculis. Sed nunc odio, lobortis in dolor ac, rutrum fermentum velit. In venenatis, velit in molestie semper, est magna condimentum dui, aliquam auctor lorem nulla vel ligula. Morbi vehicula turpis turpis, quis mollis justo tempus vitae. Proin luctus lacus in mauris porttitor aliquet. Duis vitae nunc ex. Nullam eget erat vitae nulla iaculis rhoncus. Sed lacus dui, suscipit eu leo vitae, tincidunt dignissim risus. Praesent ut massa ut arcu sagittis consequat. Sed sit amet tincidunt turpis. Nulla bibendum, felis eget posuere dictum, libero mi tristique elit, at venenatis neque elit ac quam. Curabitur nisi sem, scelerisque nec nunc ac, pulvinar pharetra odio. Curabitur egestas pellentesque arcu sed suscipit. In mattis dolor vel dui mollis feugiat. + +Sed commodo, dui ac vestibulum dictum, tellus libero tincidunt lacus, viverra commodo est felis vitae urna. Proin tincidunt neque vel turpis eleifend laoreet. Vestibulum sagittis, tortor sed iaculis consequat, urna ante sagittis est, ac ullamcorper lorem nibh dignissim odio. Nam arcu mi, cursus et blandit nec, aliquam ut nulla. Aenean quis iaculis tellus, eu egestas augue. Etiam pretium eget nisi quis iaculis. Aliquam sed convallis eros. Ut bibendum rhoncus lacus, in vestibulum dui ultricies id. Fusce vestibulum, mauris ut tempor consequat, dolor nisl pellentesque elit, in porta arcu elit vel ante. Vivamus at nisl est. Etiam nec blandit tortor, at pulvinar orci. Proin semper dapibus tincidunt. Phasellus lobortis enim ullamcorper dolor tempor cursus. + +Mauris a libero in enim gravida aliquet ac sit amet nibh. Vestibulum ac neque posuere, blandit libero ac, vehicula enim. Aliquam auctor iaculis eros sit amet molestie. Quisque faucibus turpis et massa tristique, nec dapibus mauris aliquet. Proin blandit aliquet mauris, non tincidunt odio blandit vel. Curabitur a nibh in eros commodo tincidunt eu et libero. Curabitur sit amet dapibus ex, in condimentum magna. Sed eu sem sem. Nunc tellus dolor, rutrum eu mauris nec, congue feugiat purus. Fusce tempor, neque vitae bibendum imperdiet, dolor ipsum condimentum urna, et egestas quam tortor in ex. Aliquam velit magna, commodo hendrerit sagittis sed, feugiat eget erat. Nunc quis ullamcorper velit, eu consequat augue. Nam arcu mauris, condimentum sit amet magna in, finibus scelerisque nunc. Mauris erat est, hendrerit ac accumsan eget, facilisis ut nisl. Quisque dignissim arcu quis diam tincidunt tristique. Sed rhoncus nisl non enim fermentum, a lobortis dolor consectetur. + +Sed eget condimentum ligula. Vestibulum vitae cursus eros. Donec elementum sapien magna, posuere iaculis sem ultrices lobortis. Morbi eu bibendum lectus. Suspendisse ante eros, ullamcorper ac viverra eget, pellentesque sed sapien. Duis sit amet tincidunt dui, vitae lobortis purus. Sed venenatis tincidunt volutpat. Vivamus a nisl ac elit consectetur semper ut eu libero. Proin id cursus ex. In hac habitasse platea dictumst. Aenean sed nisi vitae odio venenatis pulvinar vitae ac risus. Sed varius magna ut erat luctus vehicula. + +Nunc non ex eget purus blandit faucibus at quis velit. Donec quis mi vestibulum, facilisis nisi quis, tincidunt turpis. Sed bibendum metus sed consectetur mollis. Maecenas fermentum, erat finibus pulvinar lacinia, ex risus dictum sem, ut vestibulum augue diam vel diam. Donec ac massa non nibh pretium laoreet eget in orci. Nulla placerat eleifend mi, pretium vestibulum diam condimentum vitae. Nunc odio turpis, feugiat vitae turpis eget, pellentesque commodo turpis. Etiam sapien purus, consequat nec mi eget, consectetur efficitur neque. Phasellus porttitor sapien sit amet nunc semper, vel bibendum nibh finibus. Ut ac imperdiet ex, eu congue felis. In posuere nisi felis. Mauris tempus pretium mauris, ac viverra nunc hendrerit id. Sed fermentum nec sem ac pulvinar. Integer dictum velit eget congue venenatis. + +Cras eros dolor, venenatis ac dictum sed, dignissim nec sem. Curabitur tempor erat quis pretium interdum. Nunc vestibulum justo nisi, sit amet sagittis tellus consequat vel. Donec pharetra nunc vitae consequat eleifend. Quisque ut mauris quis nunc volutpat consectetur. Nam a suscipit ligula, at gravida libero. Vestibulum blandit, tellus sed bibendum volutpat, libero tortor convallis nisl, sit amet placerat lorem dolor nec libero. Integer blandit libero elit, ut congue ex euismod congue. Nulla sodales justo id eros condimentum faucibus. + +Proin quam ante, hendrerit at bibendum eu, pharetra at lectus. Proin finibus arcu id nisl aliquam dapibus. Fusce a suscipit nisl. Ut placerat ultrices nibh nec efficitur. Vestibulum vitae interdum magna. Donec lobortis finibus risus, et luctus ipsum efficitur euismod. Quisque dictum diam et venenatis euismod. Cras dictum molestie aliquet. Vestibulum imperdiet quam eget diam malesuada, quis pulvinar odio dignissim. Curabitur sapien elit, iaculis vitae justo eget, pretium malesuada elit. Donec gravida molestie tincidunt. Nullam at commodo ipsum. Sed nisi erat, tincidunt ultricies interdum consequat, pretium mollis ipsum. Vivamus in erat consectetur, sodales nibh at, porta nunc. Mauris semper, dui vitae condimentum tempus, mauris justo volutpat urna, ac ullamcorper tortor dolor in sem. + +Aenean leo ligula, egestas at vestibulum ac, porta vel mauris. Curabitur at lorem non mauris fringilla venenatis vel eu arcu. Donec posuere eleifend diam. Duis aliquet justo lacus, eu egestas erat eleifend sed. Sed non cursus urna. Sed pretium purus id blandit varius. Mauris turpis mi, lobortis eu tellus sit amet, maximus venenatis felis. Proin non varius enim, aliquet finibus velit. Praesent posuere pharetra ipsum eget varius. Phasellus non fermentum magna, ut iaculis augue. Praesent ut nisl nunc. + +Sed ligula tellus, interdum at sapien ut, dictum pellentesque nisl. Duis fringilla, sem nec elementum dapibus, nisl tellus maximus velit, eu varius sem nisl feugiat eros. Maecenas quis viverra lacus. Nulla nec commodo ex, nec placerat enim. Curabitur ultrices sagittis fringilla. Vestibulum sit amet enim sagittis, condimentum nisl sit amet, pulvinar orci. Ut at erat finibus erat bibendum convallis. Quisque euismod magna eget leo facilisis hendrerit. Cras venenatis, nisi quis sollicitudin volutpat, metus enim vestibulum nunc, at mollis leo leo nec est. Fusce fermentum tristique feugiat. Suspendisse sem est, condimentum ut ante at, egestas convallis ante. Vestibulum dictum, ex nec imperdiet laoreet, magna est ullamcorper augue, vel molestie quam odio vel ante. Donec dui dui, posuere a neque ac, condimentum vehicula magna. Curabitur suscipit, risus vitae bibendum posuere, mauris lorem viverra est, at tristique arcu quam quis leo. Donec sed posuere neque. Suspendisse iaculis aliquet condimentum. + +Morbi tempus condimentum diam eget bibendum. Aliquam varius magna quis lectus interdum, in elementum ligula tempor. Morbi vitae lorem sapien. Morbi luctus consectetur eros non aliquet. Pellentesque vestibulum sem sed ante accumsan faucibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ac suscipit justo. Nunc efficitur lectus eu arcu venenatis, vel accumsan ex suscipit. Vestibulum egestas ultricies tellus, et interdum enim pretium eu. Aenean rutrum est tincidunt rhoncus molestie. Phasellus hendrerit tellus et laoreet varius. Integer efficitur felis magna, nec sollicitudin arcu sollicitudin in. Curabitur non feugiat odio. Mauris nisi odio, luctus quis dolor sed, tristique luctus ex. Nullam libero enim, facilisis ut venenatis et, vulputate sed purus. + +Mauris a odio ut leo porttitor ultrices a et ex. Pellentesque vestibulum lacinia faucibus. In pellentesque eget augue at feugiat. Integer finibus augue dolor, in luctus lorem rutrum vitae. Donec pharetra lectus ac purus sollicitudin, vel tristique mauris pretium. Cras porttitor mi eu lectus consequat, a ultrices felis venenatis. In condimentum turpis in velit mattis laoreet. Aliquam sed mauris id nulla ultrices convallis vel vel velit. Suspendisse ut arcu finibus justo fermentum fringilla. Etiam in malesuada nisl. In hac habitasse platea dictumst. Duis a est lacinia, pretium dui eget, condimentum turpis. Integer ac placerat augue. Donec et eros felis. + +Integer cursus magna id quam sagittis consectetur. Aliquam erat volutpat. Quisque ullamcorper nisl nec massa dapibus facilisis vitae at nunc. Donec laoreet, libero in elementum tempus, enim odio porttitor felis, venenatis fermentum augue velit eu urna. Ut at ullamcorper enim. In molestie, velit et blandit maximus, erat nunc laoreet quam, vel finibus est mauris non sapien. Donec et dictum lorem. Nunc vestibulum, dolor eget tempus maximus, elit eros aliquam velit, vitae mattis mauris lectus ac tortor. Donec rutrum justo orci, id dictum metus vestibulum a. Etiam bibendum ipsum convallis, lacinia turpis vitae, dictum tortor. In velit dolor, scelerisque sed molestie nec, volutpat a turpis. Cras posuere commodo erat ut gravida. Quisque ante ipsum, volutpat a tellus non, dignissim ornare elit. Pellentesque sed porttitor dui, luctus rhoncus purus. In vitae ante pulvinar, consectetur orci quis, tempus velit. Curabitur tempus ligula id sapien ornare rhoncus. + +Maecenas eu nibh et elit accumsan blandit a at erat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam commodo feugiat condimentum. In non ante ut mauris eleifend lobortis. Phasellus eleifend vitae metus et semper. Nam sit amet rhoncus diam. Nunc molestie libero sed erat volutpat consequat. + +Aenean eget tristique odio. Vivamus quam tellus, dignissim sed faucibus sed, sagittis ut elit. Maecenas in ullamcorper sem. In at ipsum accumsan lectus vestibulum commodo nec non leo. Aliquam at suscipit felis. Nunc non egestas tortor. Donec sit amet eleifend eros. + +Sed eleifend nisi velit, in egestas erat condimentum eu. Pellentesque a tincidunt urna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum dapibus mauris vitae elit auctor, id venenatis sem consectetur. Vivamus non leo pulvinar, blandit libero ut, vehicula arcu. Nullam elementum ex enim, at mattis massa pharetra eu. Nunc nulla magna, lobortis a magna sit amet, laoreet fermentum justo. Curabitur aliquam sollicitudin posuere. Aenean semper porta dictum. Mauris accumsan non nisi nec faucibus augue. diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index ba4b147ae..2e68ea563 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -105,7 +105,6 @@ <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> - <version>1.9.1</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index d34dd72dd..989e88eb7 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -23,6 +23,7 @@ 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 software.amazon.lambda.powertools.idempotency.Constants; @@ -37,6 +38,8 @@ * It uses the {@link IdempotencyHandler} to actually do the job. */ @Aspect +// Idempotency annotation should come first before large message +@DeclarePrecedence("software.amazon.lambda.powertools.idempotency.internal.IdempotentAspect, *") public class IdempotentAspect { @SuppressWarnings({"EmptyMethod"}) @Pointcut("@annotation(idempotent)") diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml new file mode 100644 index 000000000..a9ded60c5 --- /dev/null +++ b/powertools-large-messages/pom.xml @@ -0,0 +1,166 @@ +<?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> + +<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> + </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> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-core</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>software.amazon.payloadoffloading</groupId> + <artifactId>payloadoffloading-common</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>utils</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</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> + + <!-- 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.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> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>${maven-surefire-plugin.version}</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> + +</project> \ No newline at end of file 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 new file mode 100644 index 000000000..758d7eb45 --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java @@ -0,0 +1,70 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Use this annotation to handle large messages (> 256 KB) 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> + * <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/> + * <u>SQS</u>:<br/> + * <pre> + * @LargeMessage + * private void processRawMessage(SQSMessage sqsMessage, Context context) { + * // sqsMessage.getBody() will contain the content of the S3 Object + * } + * </pre> + * <u>SNS</u>:<br/> + * <pre> + * @LargeMessage + * private void processMessage(SNSRecord snsRecord) { + * // snsRecord.getSNS().getMessage() will contain the content of the S3 Object + * } + * </pre> + * </p> + * + * <p>To disable the deletion of S3 objects, you can configure the {@code deleteS3Object} option to false (default is true): + * <pre> + * @LargeMessage(deleteS3Object = false) + * </pre> + * </p> + * + * <p><b>Note 1</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> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface LargeMessage { + + /** + * Specify if S3 objects must be deleted after being processed (default = true) + Alternatively you might consider using S3 lifecycle policies to remove the payloads automatically after a period of time. + */ + boolean deleteS3Object() default true; +} 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 new file mode 100644 index 000000000..fb8ea9b15 --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfig.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.largemessages; + +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_REGION_ENV; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; + +/** + * Singleton instance for Large Message Config. We need this to provide a way to customize the S3 client configuration used by the annotation. + * <br/> + * Optional: Use it in your Lambda constructor to pass a custom {@link S3Client} to the {@link software.amazon.lambda.powertools.largemessages.internal.LargeMessageProcessor} + * <br/> + * If you don't use this, a default S3Client will be created. + * <pre> + * public MyLambdaHandler() { + * LargeMessageConfig.init().withS3Client(S3Client.create()); + * } + * </pre> + */ +public class LargeMessageConfig { + + private static final LargeMessageConfig INSTANCE = new LargeMessageConfig(); + private S3Client s3Client; + + private LargeMessageConfig() { + } + + /** + * Retrieve the singleton instance (you generally don't need to use this one, used internally by the library) + * + * @return the singleton instance + */ + public static LargeMessageConfig get() { + return INSTANCE; + } + + /** + * Initialize the singleton instance + * + * @return the singleton instance + */ + public static LargeMessageConfig init() { + return INSTANCE; + } + + public void withS3Client(S3Client s3Client) { + if (this.s3Client == null) { + this.s3Client = s3Client; + } + } + + // For tests purpose + void resetS3Client() { + this.s3Client = null; + } + + // Getter needs to initialize if not done with setter + public S3Client getS3Client() { + if (this.s3Client == null) { + S3ClientBuilder s3ClientBuilder = S3Client.builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv(AWS_REGION_ENV))); + this.s3Client = s3ClientBuilder.build(); + } + return this.s3Client; + } +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageProcessingException.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageProcessingException.java new file mode 100644 index 000000000..20b19230a --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessageProcessingException.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.largemessages; + +/** + * Exception that occurs when the utility fails to retrieve the content from S3 + */ +public class LargeMessageProcessingException extends RuntimeException { + public LargeMessageProcessingException(String message, Throwable cause) { + super(message, cause); + } + + public LargeMessageProcessingException(String message) { + super(message); + } +} 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 new file mode 100644 index 000000000..861193203 --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 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; + +/** + * Handle {@link LargeMessage} annotations. + */ +@Aspect +public class LargeMessageAspect { + + private static final Logger LOG = LoggerFactory.getLogger(LargeMessageAspect.class); + + @SuppressWarnings({"EmptyMethod"}) + @Pointcut("@annotation(largeMessage)") + public void callAt(LargeMessage largeMessage) { + } + + @Around(value = "callAt(largeMessage) && execution(@LargeMessage * *.*(..))", argNames = "pjp,largeMessage") + 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); + } + + Object message = proceedArgs[0]; + Optional<LargeMessageProcessor<?>> largeMessageProcessor = LargeMessageProcessorFactory.get(message); + + if (!largeMessageProcessor.isPresent()) { + LOG.warn("@LargeMessage annotation is placed on a method with unsupported message type [{}], proceeding", message.getClass()); + return pjp.proceed(proceedArgs); + } + + return largeMessageProcessor.get().process(pjp, largeMessage.deleteS3Object()); + } + +} 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 new file mode 100644 index 000000000..f0e89e631 --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.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.largemessages.internal; + +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; +import software.amazon.lambda.powertools.largemessages.LargeMessageProcessingException; +import software.amazon.payloadoffloading.S3BackedPayloadStore; +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. + * + * @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) + */ +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]; + + if (!isLargeMessage(message)) { + LOG.warn("Not a large message, proceeding"); + return pjp.proceed(proceedArgs); + } + + String payloadPointer = getMessageContent(message); + if (payloadPointer == null) { + LOG.warn("No content in the message, proceeding"); + return pjp.proceed(proceedArgs); + } + // legacy attribute (sqs only) + payloadPointer = payloadPointer.replace("com.amazon.sqs.javamessaging.MessageS3Pointer", "software.amazon.payloadoffloading.PayloadS3Pointer"); + + if (LOG.isInfoEnabled()) { + LOG.info("Large message [{}]: retrieving content from S3", getMessageId(message)); + } + + String s3ObjectContent = getS3ObjectContent(payloadPointer); + + if (LOG.isDebugEnabled()) { + LOG.debug("Large message [{}] retrieved in S3 [{}]: {}KB", getMessageId(message), payloadPointer, + s3ObjectContent.getBytes(StandardCharsets.UTF_8).length / 1024); + } + + updateMessageContent(message, s3ObjectContent); + removeLargeMessageAttributes(message); + + Object response = pjp.proceed(proceedArgs); + + if (deleteS3Object) { + if (LOG.isInfoEnabled()) { + LOG.info("Large message [{}]: deleting object from S3", getMessageId(message)); + } + deleteS3Object(payloadPointer); + } + + return response; + } + + /** + * Retrieve the message id + * + * @param message the message itself + * @return the id of the message (String format) + */ + protected abstract String getMessageId(T message); + + /** + * Retrieve the content of the message (ex: body of an SQSMessage) + * + * @param message the message itself + * @return the content of the message (String format) + */ + protected abstract String getMessageContent(T message); + + /** + * Update the message content of the message (ex: body of an SQSMessage) + * + * @param message the message itself + * @param messageContent the new content of the message (String format) + */ + protected abstract void updateMessageContent(T message, String messageContent); + + /** + * Check if the message is actually a large message (indicator in message attributes) + * + * @param message the message itself + * @return true if the message is a large message + */ + protected abstract boolean isLargeMessage(T message); + + /** + * Remove the large message indicator (in message attributes) + * + * @param message the message itself + */ + protected abstract void removeLargeMessageAttributes(T message); + + private String getS3ObjectContent(String payloadPointer) { + try { + return payloadStore.getOriginalPayload(payloadPointer); + } catch (SdkException e) { + throw new LargeMessageProcessingException(format("Failed processing S3 record [%s]", payloadPointer), e); + } + } + + private void deleteS3Object(String payloadPointer) { + try { + payloadStore.deleteOriginalPayload(payloadPointer); + } catch (SdkException e) { + throw new LargeMessageProcessingException(format("Failed deleting S3 record [%s]", payloadPointer), e); + } + } + +} 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 new file mode 100644 index 000000000..26c33738a --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.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.largemessages.internal; + +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import java.util.Optional; + +class LargeMessageProcessorFactory { + + private LargeMessageProcessorFactory() { + // not intended to be instantiated + } + + public static Optional<LargeMessageProcessor<?>> get(Object message) { + if (message instanceof SQSMessage) { + return Optional.of(new LargeSQSMessageProcessor()); + } else if (message instanceof SNSRecord) { + return Optional.of(new LargeSNSMessageProcessor()); + } else { + return Optional.empty(); + } + } +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSNSMessageProcessor.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSNSMessageProcessor.java new file mode 100644 index 000000000..1ed7f5eaa --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSNSMessageProcessor.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.largemessages.internal; + +import com.amazonaws.services.lambda.runtime.events.SNSEvent.MessageAttribute; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; +import java.util.HashMap; +import java.util.Map; + +class LargeSNSMessageProcessor extends LargeMessageProcessor<SNSRecord> { + + @Override + protected String getMessageId(SNSRecord message) { + return message.getSNS().getMessageId(); + } + + @Override + protected String getMessageContent(SNSRecord message) { + return message.getSNS().getMessage(); + } + + @Override + protected void updateMessageContent(SNSRecord message, String messageContent) { + message.getSNS().setMessage(messageContent); + } + + @Override + protected boolean isLargeMessage(SNSRecord message) { + Map<String, MessageAttribute> msgAttributes = message.getSNS().getMessageAttributes(); + return msgAttributes != null && msgAttributes.containsKey(RESERVED_ATTRIBUTE_NAME); + } + + @Override + protected void removeLargeMessageAttributes(SNSRecord message) { + // message.getSNS().getMessageAttributes() does not support remove operation, copy to new map + Map<String, MessageAttribute> newAttributes = new HashMap<>(message.getSNS().getMessageAttributes()); + newAttributes.remove(RESERVED_ATTRIBUTE_NAME); + message.getSNS().setMessageAttributes(newAttributes); + } +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java new file mode 100644 index 000000000..18c99e300 --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java @@ -0,0 +1,173 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.utils.BinaryUtils; +import software.amazon.awssdk.utils.Md5Utils; + +class LargeSQSMessageProcessor extends LargeMessageProcessor<SQSMessage> { + + private static final Logger LOG = LoggerFactory.getLogger(LargeSQSMessageProcessor.class); + private static final String LEGACY_RESERVED_ATTRIBUTE_NAME = "SQSLargePayloadSize"; + private static final int INTEGER_SIZE_IN_BYTES = 4; + private static final byte STRING_TYPE_FIELD_INDEX = 1; + private static final byte BINARY_TYPE_FIELD_INDEX = 2; + private static final byte STRING_LIST_TYPE_FIELD_INDEX = 3; + private static final byte BINARY_LIST_TYPE_FIELD_INDEX = 4; + + @Override + protected String getMessageId(SQSMessage message) { + return message.getMessageId(); + } + + @Override + protected String getMessageContent(SQSMessage message) { + return message.getBody(); + } + + @Override + protected void updateMessageContent(SQSMessage message, String messageContent) { + message.setBody(messageContent); + // we update the MD5 digest so it doesn't look tempered + message.setMd5OfBody(calculateMessageBodyMd5(messageContent).orElse(message.getMd5OfBody())); + } + + @Override + protected boolean isLargeMessage(SQSMessage message) { + Map<String, MessageAttribute> msgAttributes = message.getMessageAttributes(); + return msgAttributes != null && (msgAttributes.containsKey(RESERVED_ATTRIBUTE_NAME) || msgAttributes.containsKey(LEGACY_RESERVED_ATTRIBUTE_NAME)); + } + + @Override + protected void removeLargeMessageAttributes(SQSMessage message) { + // message.getMessageAttributes() does not support remove operation, copy to new map + Map<String, MessageAttribute> newAttributes = new HashMap<>(message.getMessageAttributes()); + newAttributes.remove(RESERVED_ATTRIBUTE_NAME); + newAttributes.remove(LEGACY_RESERVED_ATTRIBUTE_NAME); + message.setMessageAttributes(newAttributes); + // we update the MD5 digest so it doesn't look tempered + message.setMd5OfMessageAttributes(calculateMessageAttributesMd5(newAttributes).orElse(message.getMd5OfMessageAttributes())); + } + + /** + * Compute the MD5 of the message body.<br/> + * Inspired from {@code software.amazon.awssdk.services.sqs.internal.MessageMD5ChecksumInterceptor}.<br/> + * package protected for testing purpose. + * + * @param messageBody body of the SQS Message + * @return the MD5 digest of the SQS Message body (or empty in case of error) + */ + static Optional<String> calculateMessageBodyMd5(String messageBody) { + byte[] expectedMd5; + try { + expectedMd5 = Md5Utils.computeMD5Hash(messageBody.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + LOG.warn("Unable to calculate the MD5 hash of the message body. ", e); + return Optional.empty(); + } + return Optional.of(BinaryUtils.toHex(expectedMd5)); + } + + /** + * Compute the MD5 of the message attributes.<br/> + * Inspired from {@code software.amazon.awssdk.services.sqs.internal.MessageMD5ChecksumInterceptor}.<br/> + * package protected for testing purpose. + * + * @param messageAttributes attributes of the SQS Message + * @return the MD5 digest of the SQS Message attributes (or empty in case of error) + */ + @SuppressWarnings("squid:S4790") // MD5 algorithm is used by SQS, we must use MD5 + static Optional<String> calculateMessageAttributesMd5(final Map<String, MessageAttribute> messageAttributes) { + List<String> sortedAttributeNames = new ArrayList<>(messageAttributes.keySet()); + Collections.sort(sortedAttributeNames); + + MessageDigest md5Digest; + try { + md5Digest = MessageDigest.getInstance("MD5"); + + for (String attrName : sortedAttributeNames) { + MessageAttribute attrValue = messageAttributes.get(attrName); + + // Encoded Name + updateLengthAndBytes(md5Digest, attrName); + + // Encoded Type + updateLengthAndBytes(md5Digest, attrValue.getDataType()); + + // Encoded Value + if (attrValue.getStringValue() != null) { + md5Digest.update(STRING_TYPE_FIELD_INDEX); + updateLengthAndBytes(md5Digest, attrValue.getStringValue()); + } else if (attrValue.getBinaryValue() != null) { + md5Digest.update(BINARY_TYPE_FIELD_INDEX); + updateLengthAndBytes(md5Digest, attrValue.getBinaryValue()); + } else if (attrValue.getStringListValues() != null && + attrValue.getStringListValues().size() > 0) { + md5Digest.update(STRING_LIST_TYPE_FIELD_INDEX); + for (String strListMember : attrValue.getStringListValues()) { + updateLengthAndBytes(md5Digest, strListMember); + } + } else if (attrValue.getBinaryListValues() != null && + attrValue.getBinaryListValues().size() > 0) { + md5Digest.update(BINARY_LIST_TYPE_FIELD_INDEX); + for (ByteBuffer byteListMember : attrValue.getBinaryListValues()) { + updateLengthAndBytes(md5Digest, byteListMember); + } + } + } + } catch (Exception e) { + LOG.warn("Unable to calculate the MD5 hash of the message attributes. ", e); + return Optional.empty(); + } + + return Optional.of(BinaryUtils.toHex(md5Digest.digest())); + } + + /** + * Update the digest using a sequence of bytes that consists of the length (in 4 bytes) of the + * input String and the actual utf8-encoded byte values. + */ + private static void updateLengthAndBytes(MessageDigest digest, String str) { + byte[] utf8Encoded = str.getBytes(StandardCharsets.UTF_8); + ByteBuffer lengthBytes = ByteBuffer.allocate(INTEGER_SIZE_IN_BYTES).putInt(utf8Encoded.length); + digest.update(lengthBytes.array()); + digest.update(utf8Encoded); + } + + /** + * Update the digest using a sequence of bytes that consists of the length (in 4 bytes) of the + * input ByteBuffer and all the bytes it contains. + */ + private static void updateLengthAndBytes(MessageDigest digest, ByteBuffer binaryValue) { + ByteBuffer readOnlyBuffer = binaryValue.asReadOnlyBuffer(); + int size = readOnlyBuffer.remaining(); + ByteBuffer lengthBytes = ByteBuffer.allocate(INTEGER_SIZE_IN_BYTES).putInt(size); + digest.update(lengthBytes.array()); + digest.update(readOnlyBuffer); + } +} diff --git a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfigTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfigTest.java new file mode 100644 index 000000000..b6bcaf6b5 --- /dev/null +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessageConfigTest.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.largemessages; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +public class LargeMessageConfigTest { + + @BeforeEach + public void setup() { + LargeMessageConfig.get().resetS3Client(); + } + + @AfterEach + public void tearDown() { + LargeMessageConfig.get().resetS3Client(); + } + + @Test + public void singleton_shouldNotChangeWhenCalledMultipleTimes() { + LargeMessageConfig.init().withS3Client(S3Client.builder().region(Region.US_EAST_1).build()); + LargeMessageConfig config = LargeMessageConfig.get(); + + LargeMessageConfig.init().withS3Client(null); + LargeMessageConfig config2 = LargeMessageConfig.get(); + + assertThat(config2).isEqualTo(config); + } + + @Test + public void singletonWithDefaultClient_shouldNotChangeWhenCalledMultipleTimes() { + S3Client s3Client = LargeMessageConfig.get().getS3Client(); + + LargeMessageConfig.init().withS3Client(S3Client.create()); + S3Client s3Client2 = LargeMessageConfig.get().getS3Client(); + + assertThat(s3Client2).isEqualTo(s3Client); + } +} 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 new file mode 100644 index 000000000..95dfd445a --- /dev/null +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java @@ -0,0 +1,333 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 static java.lang.String.format; +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 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; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +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.mockito.ArgumentCaptor; +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.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.lambda.powertools.largemessages.LargeMessage; +import software.amazon.lambda.powertools.largemessages.LargeMessageConfig; +import software.amazon.lambda.powertools.largemessages.LargeMessageProcessingException; + +public class LargeMessageAspectTest { + + private static final String BIG_MSG = "A biiiiiiiig message"; + private static final String BIG_MSG_MD5 = "919ebd392d8cb7161f95cb612a903d42"; + + 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; + @Mock + private Context context; + + @BeforeEach + public void init() throws NoSuchFieldException, IllegalAccessException { + openMocks(this); + setupContext(); + // 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); + } + + @LargeMessage + private String processSQSMessage(SQSMessage sqsMessage, Context context) { + return sqsMessage.getBody(); + } + + @LargeMessage + private String processSQSMessageWithMd5Checks(SQSMessage transformedMessage, String initialBodyMD5, String initialAttributesMD5) { + assertThat(transformedMessage.getMd5OfBody()).isNotEqualTo(initialBodyMD5); + assertThat(transformedMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); + + assertThat(transformedMessage.getMessageAttributes()).hasSize(3); + + assertThat(transformedMessage.getMd5OfMessageAttributes()).isNotEqualTo(initialAttributesMD5); + return transformedMessage.getBody(); + } + + @LargeMessage + private String processSNSMessageWithoutContext(SNSRecord snsRecord) { + return snsRecord.getSNS().getMessage(); + } + + @LargeMessage(deleteS3Object = false) + private String processSQSMessageNoDelete(SQSMessage sqsMessage, Context context) { + return sqsMessage.getBody(); + } + + @LargeMessage + private String processKinesisMessage(KinesisEventRecord kinesisEventRecord) { + return kinesisEventRecord.getEventID(); + } + + @LargeMessage + private String processNoMessage() { + return "Hello World"; + } + + @Test + public void testLargeSQSMessageWithDefaultDeletion() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when + String message = processSQSMessage(sqsMessage, context); + + // then + assertThat(message).isEqualTo(BIG_MSG); + 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 testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + + MessageAttribute stringListAttribute = new MessageAttribute(); + stringListAttribute.setStringListValues(Collections.singletonList("customAttributeValue")); + stringListAttribute.setDataType("StringList"); + + MessageAttribute binAttribute = new MessageAttribute(); + binAttribute.setBinaryValue(ByteBuffer.wrap("customAttributeValue".getBytes(StandardCharsets.UTF_8))); + binAttribute.setDataType("Binary"); + + MessageAttribute listBinAttribute = new MessageAttribute(); + listBinAttribute.setBinaryListValues(Collections.singletonList(ByteBuffer.wrap("customAttributeValue".getBytes(StandardCharsets.UTF_8)))); + listBinAttribute.setDataType("BinaryList"); + + Map<String, MessageAttribute> attrs = new HashMap<>(); + attrs.put("stringListAttribute", stringListAttribute); + attrs.put("binAttribute", binAttribute); + attrs.put("listBinAttribute", listBinAttribute); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true, attrs); + + // when + String message = processSQSMessageWithMd5Checks(sqsMessage, sqsMessage.getMd5OfBody(), sqsMessage.getMd5OfMessageAttributes()); + + // then + assertThat(message).isEqualTo(BIG_MSG); + } + + @Test + public void testLargeSNSMessageWithDefaultDeletion() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SNSRecord snsRecord = snsRecordWithMessage(BIG_MESSAGE_BODY, true); + + //when + String message = processSNSMessageWithoutContext(snsRecord); + + // then + assertThat(message).isEqualTo(BIG_MSG); + 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 testLargeSQSMessageWithNoDeletion_shouldNotDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when + String message = processSQSMessageNoDelete(sqsMessage, context); + + // then + assertThat(message).isEqualTo(BIG_MSG); + verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + public void testKinesisMessage_shouldProceedWithoutS3() { + // given + KinesisEventRecord kinesisEventRecord = new KinesisEventRecord(); + kinesisEventRecord.setEventID("kinesis_id1234567890"); + + // when + String message = processKinesisMessage(kinesisEventRecord); + + // then + assertThat(message).isEqualTo("kinesis_id1234567890"); + verifyNoInteractions(s3Client); + } + + @Test + public void testNoMessage_shouldProceedWithoutS3() { + // when + String message = processNoMessage(); + + // then + assertThat(message).isEqualTo("Hello World"); + verifyNoInteractions(s3Client); + } + + @Test + public void testSmallMessage_shouldProceedWithoutS3() { + // given + SQSMessage sqsMessage = sqsMessageWithBody("This is small message", false); + + // when + String message = processSQSMessage(sqsMessage, context); + + // then + assertThat(message) + .isEqualTo("This is small message"); + verifyNoInteractions(s3Client); + } + + @Test + public void testNullMessage_shouldProceedWithoutS3() { + // given + SQSMessage sqsMessage = sqsMessageWithBody(null, true); + + // when + String message = processSQSMessage(sqsMessage, context); + + // then + assertThat(message).isNull(); + verifyNoInteractions(s3Client); + } + + @Test + public 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))); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> processSQSMessage(sqsMessage, context)) + .isInstanceOf(LargeMessageProcessingException.class) + .hasMessage(format("Failed processing S3 record [%s]", BIG_MESSAGE_BODY)); + } + + @Test + public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingException() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + when(s3Client.deleteObject(any(DeleteObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", new Exception("User is not allowed to access bucket " + BUCKET_NAME))); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> processSQSMessage(sqsMessage, context)) + .isInstanceOf(LargeMessageProcessingException.class) + .hasMessage(format("Failed deleting S3 record [%s]", BIG_MESSAGE_BODY)); + } + + private ResponseInputStream<GetObjectResponse> s3ObjectWithLargeMessage() { + return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); + } + + private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage) { + return sqsMessageWithBody(messageBody, largeMessage, null); + } + + private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, Map<String, MessageAttribute> optionalAttributes) { + SQSMessage sqsMessage = new SQSMessage(); + sqsMessage.setBody(messageBody); + if (messageBody != null) { + sqsMessage.setMd5OfBody(calculateMessageBodyMd5(messageBody).orElseThrow(() -> new RuntimeException("Unable to md5 body " + messageBody))); + } + + if (largeMessage) { + Map<String, MessageAttribute> attributeMap = new HashMap<>(); + if (optionalAttributes != null) { + attributeMap.putAll(optionalAttributes); + } + MessageAttribute payloadAttribute = new MessageAttribute(); + payloadAttribute.setStringValue("300450"); + payloadAttribute.setDataType("Number"); + attributeMap.put(LargeMessageProcessor.RESERVED_ATTRIBUTE_NAME, payloadAttribute); + + sqsMessage.setMessageAttributes(attributeMap); + sqsMessage.setMd5OfMessageAttributes(calculateMessageAttributesMd5(attributeMap).orElseThrow(() -> new RuntimeException("Unable to md5 attributes " + attributeMap))); + } + return sqsMessage; + } + + private SNSRecord snsRecordWithMessage(String messageBody, boolean largeMessage) { + SNS sns = new SNS().withMessage(messageBody); + if (largeMessage) { + sns.setMessageAttributes(Collections.singletonMap(LargeMessageProcessor.RESERVED_ATTRIBUTE_NAME, new SNSEvent.MessageAttribute())); + } + 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/LargeMessageProcessorFactoryTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactoryTest.java new file mode 100644 index 000000000..3011c8189 --- /dev/null +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactoryTest.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.largemessages.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import org.junit.jupiter.api.Test; + +public class LargeMessageProcessorFactoryTest { + + @Test + public void createLargeSQSMessageProcessor() { + assertThat(LargeMessageProcessorFactory.get(new SQSEvent.SQSMessage())) + .isPresent() + .get() + .isInstanceOf(LargeSQSMessageProcessor.class); + } + + @Test + public void createLargeSNSMessageProcessor() { + assertThat(LargeMessageProcessorFactory.get(new SNSEvent.SNSRecord())) + .isPresent() + .get() + .isInstanceOf(LargeSNSMessageProcessor.class); + } + + @Test + public void createUnknownMessageProcessor() { + assertThat(LargeMessageProcessorFactory.get(new KinesisEvent.KinesisEventRecord())).isNotPresent(); + } +} diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index d8afac783..c21943fba 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -29,6 +29,7 @@ <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> 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 index 847dd456c..a3a92cea1 100644 --- 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 @@ -20,9 +20,12 @@ import java.lang.annotation.Target; /** - * {@code SqsLargeMessage} is used to signal that the annotated method + * @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 + * 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} @@ -73,6 +76,7 @@ * <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 { 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 index 8c06a6291..ace2a42d6 100644 --- 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 @@ -36,7 +36,11 @@ /** * 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 { private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 5a5e3bed8..eca7e266f 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -36,6 +36,10 @@ <Class name="software.amazon.lambda.powertools.idempotency.Idempotency"/> <Method name="getConfig"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.largemessages.LargeMessageConfig"/> + <Method name="getS3Client"/> + </And> </Or> </Match> <Match> @@ -93,6 +97,10 @@ <Class name="software.amazon.lambda.powertools.utilities.EventDeserializer$EventPart"/> <Field name="contentList"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.largemessages.LargeMessageConfig"/> + <Field name="s3Client"/> + </And> </Or> </Match> <!--Functionally needed--> From 6900b721191d1d89f0a896dcbc15822384bcc1f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:05:29 +0200 Subject: [PATCH 0409/1008] build(deps): bump commons-io:commons-io from 2.11.0 to 2.13.0 (#1333) Bumps commons-io:commons-io from 2.11.0 to 2.13.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 500b7f30a..2c802edc3 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -101,7 +101,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.11.0</version> + <version>2.13.0</version> </dependency> <dependency> From 7179713e211d7c3c6357423b16ab65cf9b8490f9 Mon Sep 17 00:00:00 2001 From: Eleni Dimitropoulou <12170229+eldimi@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:25:02 +0300 Subject: [PATCH 0410/1008] chore: Add powertools specific user-agent-suffix to the AWS SDK v2 clients (#1306) --- powertools-core/pom.xml | 11 ++ .../core/internal/UserAgentConfigurator.java | 111 ++++++++++++++++ .../resources-filtered/version.properties | 9 ++ .../internal/UserAgentConfiguratorTest.java | 122 ++++++++++++++++++ .../src/test/resources/test.properties | 1 + .../src/test/resources/unreadable.properties | 2 + .../persistence/DynamoDBPersistenceStore.java | 24 ++-- .../parameters/AppConfigProvider.java | 5 + .../powertools/parameters/BaseProvider.java | 1 + .../parameters/DynamoDbProvider.java | 4 + .../powertools/parameters/SSMProvider.java | 4 + .../parameters/SecretsProvider.java | 4 + .../lambda/powertools/sqs/SqsUtils.java | 18 ++- 13 files changed, 304 insertions(+), 12 deletions(-) create mode 100644 powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.java create mode 100644 powertools-core/src/main/resources-filtered/version.properties create mode 100644 powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java create mode 100644 powertools-core/src/test/resources/test.properties create mode 100644 powertools-core/src/test/resources/unreadable.properties diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 1adb64af8..78cd735b9 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -64,6 +64,11 @@ <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> @@ -104,6 +109,12 @@ </dependencies> <build> + <resources> + <resource> + <directory>src/main/resources-filtered</directory> + <filtering>true</filtering> + </resource> + </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.java new file mode 100644 index 000000000..354305d33 --- /dev/null +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/UserAgentConfigurator.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 software.amazon.lambda.powertools.core.internal; + +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; + +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +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 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 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/" + + PT_EXEC_ENV_VARIABLE; + + private UserAgentConfigurator() { + throw new IllegalStateException("Utility class. Not meant to be instantiated"); + } + + /** + * Retrieves the project version from the version.properties file + * + * @return the project version + */ + 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. + * The version is retrieved from the property with the given key. + * + * @param propertyFileName the name of the properties file + * @param versionKey the key of the property that contains the version + * @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())) { + Properties properties = new Properties(); + properties.load(fis); + 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); + } + } + return NA; + } + + /** + * Retrieves the user agent string for the Powertools for AWS Lambda. + * 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" + * + * @param ptFeature a custom feature to be added to the user agent string (e.g. idempotency). + * If null or empty, the default PT_FEATURE is used. + * The default PT_FEATURE is "no-op". + * @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); + + if (ptFeature == null || ptFeature.isEmpty()) { + ptFeature = NO_OP; + } + return userAgent + .replace(PT_FEATURE_VARIABLE, ptFeature) + .replace(PT_EXEC_ENV_VARIABLE, ptExecEnv); + } +} diff --git a/powertools-core/src/main/resources-filtered/version.properties b/powertools-core/src/main/resources-filtered/version.properties new file mode 100644 index 000000000..5e95fb588 --- /dev/null +++ b/powertools-core/src/main/resources-filtered/version.properties @@ -0,0 +1,9 @@ +# The filtered properties can have variables that are filled in by system properties or project properties. +# See https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html +# +# The values are replaced before copying the resources to the main output directory. Therefore, as soon as the build phase is completed, +# the values should have been replaced if the properties are available and if 'filtering' is set to true in the pom.xml +# +# +# The ${project.version} is retrieved from the respective pom.xml property +powertools.version=${project.version} \ 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 new file mode 100644 index 000000000..00110077f --- /dev/null +++ b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/test.properties b/powertools-core/src/test/resources/test.properties new file mode 100644 index 000000000..65756b8dd --- /dev/null +++ b/powertools-core/src/test/resources/test.properties @@ -0,0 +1 @@ +powertools.version= \ No newline at end of file diff --git a/powertools-core/src/test/resources/unreadable.properties b/powertools-core/src/test/resources/unreadable.properties new file mode 100644 index 000000000..42ff4693f --- /dev/null +++ b/powertools-core/src/test/resources/unreadable.properties @@ -0,0 +1,2 @@ +# 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-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 7a023b4de..d7301b149 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -14,6 +14,18 @@ package software.amazon.lambda.powertools.idempotency.persistence; +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.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.lambda.powertools.core.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 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; @@ -25,11 +37,7 @@ import java.util.OptionalLong; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -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.ConditionalCheckFailedException; import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; @@ -37,10 +45,6 @@ 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.idempotency.Constants; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; /** * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.<br> @@ -49,6 +53,7 @@ public class DynamoDBPersistenceStore extends BasePersistenceStore implements PersistenceStore { private static final Logger LOG = LoggerFactory.getLogger(DynamoDBPersistenceStore.class); + public static final String IDEMPOTENCY = "idempotency"; private final String tableName; private final String keyAttr; @@ -92,6 +97,7 @@ private DynamoDBPersistenceStore(String tableName, if (idempotencyDisabledEnv == null || "false".equalsIgnoreCase(idempotencyDisabledEnv)) { this.dynamoDbClient = DynamoDbClient.builder() .httpClient(UrlConnectionHttpClient.builder().build()) + .overrideConfiguration(ClientOverrideConfiguration.builder().putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurator.getUserAgent(IDEMPOTENCY)).build()) .region(Region.of(System.getenv(AWS_REGION_ENV))) .build(); } else { 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 index 130be25a3..0df05f875 100644 --- 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 @@ -17,12 +17,15 @@ 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; @@ -153,6 +156,8 @@ public AppConfigProvider build() { 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(); } 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 e7bfdf835..e6481c5da 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 @@ -31,6 +31,7 @@ */ @NotThreadSafe public abstract class BaseProvider implements ParamProvider { + public static final String PARAMETERS = "parameters"; protected final CacheManager cacheManager; private TransformationManager transformationManager; 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 index 2b0694a5d..499241927 100644 --- 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 @@ -18,6 +18,8 @@ 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; @@ -26,6 +28,7 @@ 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; @@ -132,6 +135,7 @@ 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(); } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index b24b1e319..4cfd8f899 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -18,6 +18,8 @@ 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; @@ -25,6 +27,7 @@ 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.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; @@ -248,6 +251,7 @@ 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(); } 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 index 99b87f84b..788367ea8 100644 --- 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 @@ -20,10 +20,13 @@ 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; @@ -158,6 +161,7 @@ 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(); } 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 index ace2a42d6..c838180fd 100644 --- 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 @@ -27,8 +27,11 @@ 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; @@ -42,10 +45,10 @@ */ @Deprecated public final class SqsUtils { - private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); + public static final String SQS = "sqs"; + private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); private static final ObjectMapper objectMapper = new ObjectMapper(); - // The attribute on an SQS-FIFO message used to record the message group ID private static final String MESSAGE_GROUP_ID = "MessageGroupId"; private static SqsClient client; private static S3Client s3Client; @@ -497,7 +500,11 @@ public static <R> List<R> batchProcessor(final SQSEvent event, final List<R> handlerReturn = new ArrayList<>(); if (client == null) { - client = SqsClient.create(); + client = (SqsClient) SqsClient.builder() + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(SQS)) + .build()); } BatchContext batchContext = new BatchContext(client); @@ -586,6 +593,11 @@ public static ObjectMapper 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(); } From 7f6c8921d50dfe41cd62e06399c51a772c445f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 3 Aug 2023 08:56:54 +0200 Subject: [PATCH 0411/1008] docs: improve contributing guide (#1334) * contributing guide improved --- CONTRIBUTING.md | 121 +++++++++++++++++++++------ docs/media/intellij_checkstyle_1.png | Bin 0 -> 549697 bytes docs/media/intellij_checkstyle_2.png | Bin 0 -> 460656 bytes docs/media/intellij_checkstyle_3.png | Bin 0 -> 101689 bytes 4 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 docs/media/intellij_checkstyle_1.png create mode 100644 docs/media/intellij_checkstyle_2.png create mode 100644 docs/media/intellij_checkstyle_3.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46fab27cf..8db303737 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,59 +1,128 @@ +<!-- markdownlint-disable MD043 MD041 --> +# Table of contents <!-- omit in toc --> + +- [Contributing Guidelines](#contributing-guidelines) + - [Reporting Bugs/Feature Requests](#reporting-bugsfeature-requests) + - [Contributing via Pull Requests](#contributing-via-pull-requests) + - [Dev setup](#dev-setup) + - [Local documentation](#local-documentation) + - [Conventions](#conventions) + - [General terminology and practices](#general-terminology-and-practices) + - [Testing definition](#testing-definition) + - [Finding contributions to work on](#finding-contributions-to-work-on) + - [Code of Conduct](#code-of-conduct) + - [Security issue notifications](#security-issue-notifications) + - [Troubleshooting](#troubleshooting) + - [Licensing](#licensing) + # Contributing Guidelines -Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional -documentation, we greatly value feedback and contributions from our community. +<!-- markdownlint-disable MD013 --> +Thank you for your interest in contributing to our project. Whether it's a [bug report](https://github.com/aws-powertools/powertools-lambda-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 --> 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. - ## Reporting Bugs/Feature Requests -We welcome you to use the GitHub issue tracker to report bugs or suggest features. - -When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already -reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: - -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment +We welcome you to use the GitHub issue tracker to report bugs, suggest features, or documentation improvements. +<!-- markdownlint-disable MD013 --> +[When filing an issue](https://github.com/aws-powertools/powertools-lambda-java/issues/new/choose), please check [existing open](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc), or [recently closed](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. +<!-- markdownlint-enable MD013 --> ## Contributing via Pull Requests + Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *main* branch. -2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. -3. You open an issue to discuss any significant work - we would hate for your time to be wasted. +1. You are working on a fork. [Fork the repository](https://github.com/aws-powertools/powertools-lambda-java/fork). +2. You are working against the latest source on the **main** branch. +3. You've checked existing open, and recently merged pull requests to make sure someone else hasn't addressed the problem already. +4. You've opened an [issue](https://github.com/aws-powertools/powertools-lambda-java/issues/new/choose) before you begin any implementation. We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. + +### Dev setup + +We recommend using [IntelliJ IDEA](https://www.jetbrains.com/idea/) from JetBrains. +A community version is available and largely enough for our purpose. + +#### Code Formatting + +We strongly recommend installing the CheckStyle-IDEA plugin and apply the provided [checkstyle.xml](checkstyle.xml) in order to comply with our formatting rules: + +1. Install the [CheckStyle-IDEA plugin](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea) and restart IntelliJ. + +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) + +4. Apply the reformat, optimize imports, rearrange and cleanup to your code and only to java files: +![](docs/media/intellij_checkstyle_3.png) + +#### License headers +All the java files should contain the licence/copyright header. You can copy paste it from the [license-header](license-header) file. -To send us a pull request, please: +### Creating the pull request -1. Fork the repository. -2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass: `mvn clean test` -4. Ensure your code is formatted with the provided [checkstyle.xml](https://github.com/aws-powertools/powertools-lambda-java/blob/main/checkstyle.xml): `mvn clean verify` -5. Commit to your fork using clear commit messages. -6. Send us a pull request, answering any default questions in the pull request interface. -7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +To send us a pull request, please follow these steps: + +1. Create a new branch to focus on the specific change you are contributing e.g. `improv/logger-debug-sampling` +2. Run all tests, and code baseline checks: `mvn clean verify -P build-with-spotbugs` +3. Commit to your fork using clear commit messages. +4. Send us a pull request with a [conventional semantic title](.github/semantic.yml), and answering any default questions in the pull request interface. +5. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). +### Local documentation + +If you work on the documentation, you may find useful to display it locally while editing, using the following command: + +- **Docs website**: `make docs-local-docker` + +## Conventions + +### General terminology and practices + +| Category | Convention | +|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Style guide** | We use Checkstyle and Sonar to enforce beyond good practices. | +| **Exceptions** | Specific exceptions live within the utilities themselves and use `Exception` suffix e.g. `IdempotencyKeyException`. | +| **Git commits** | We follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). We do not enforce conventional commits on contributors to lower the entry bar. Instead, we enforce a conventional PR title so our label automation and changelog are generated correctly. | +| **API documentation** | API reference docs are generated from Javadoc which should have examples to allow developers to have what they need within their own IDE. Documentation website covers the wider usage, tips, and strive to be concise. | +| **Documentation** | We treat it like a product. We sub-divide content aimed at getting started (80% of customers) vs advanced usage (20%). We also ensure customers know how to unit test their code when using our features. | + +### Testing definition + +We group tests in different categories + +| Test | When to write | Notes | Speed | +| ----------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | +| Unit tests | Verify the smallest possible unit works. | Networking access is prohibited. Prefer Functional tests given our complexity. | Lightning fast (nsec to ms) | +| End-to-end tests | Gain confidence that a Lambda function with our code operates as expected. | It simulates how customers configure, deploy, and run their Lambda function - Event Source configuration, IAM permissions, etc. | Slow (minutes) | + +**NOTE**: Unit tests are mandatory. End-to-end tests should be created for new modules. +We apply the principle of the [test pyramid](https://martinfowler.com/articles/practical-test-pyramid.html), having a majority of unit tests to cover most cases (standard, edge, errors, ...) and generally one end-to-end test to verify the standard case in the target environment. ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/help wanted/invalid/question/documentation), [looking at any 'good first issue' issues is a great place to start](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). ## Code of Conduct + This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. - +<opensource-codeofconduct@amazon.com> with any additional questions or comments. ## Security issue notifications + If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. +## Troubleshooting + ## Licensing diff --git a/docs/media/intellij_checkstyle_1.png b/docs/media/intellij_checkstyle_1.png new file mode 100644 index 0000000000000000000000000000000000000000..322e247443111074748c9d65268641d3a929f85a GIT binary patch literal 549697 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%0DqtIS$T;HpVr4dI{GQ=HB+OQ-Y!LLNEH^+R^K8`y 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/Sj+7@wmL0;ktW=*R-PIn(kVmdl#f$>x09O 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 ka{)E4VC*}1Q^Mh4Zlbet;n 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 literal 0 HcmV?d00001 diff --git a/docs/media/intellij_checkstyle_2.png b/docs/media/intellij_checkstyle_2.png new file mode 100644 index 0000000000000000000000000000000000000000..7fc82187b209169dd1b8307741bcd19b06d76804 GIT binary patch literal 460656 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%0A_g+flF*asZuZ&j3sR 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ŠlT<4rRJ{g@!?y_-zvN1Qs>(o2ju^0$NZ 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 Q_A8bd1}y=-UA~{iNyW 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%0Dl*m|7F8E>`$;l>75-3#%n@X&u7#KTAPC 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 literal 0 HcmV?d00001 diff --git a/docs/media/intellij_checkstyle_3.png b/docs/media/intellij_checkstyle_3.png new file mode 100644 index 0000000000000000000000000000000000000000..6e08dde623be7ec2a1e961d8ce8dfe9df5f52cca GIT binary patch literal 101689 zcmeFYWl)^m(k=`PgKKcNkl+mNFgPSwaCdjt;1-+&m*DOW!7aE2cXxM}Lmt`tdG~kz zyj9=%x2YOxrdRj1y06vCdhQ94lMzQoz(;_9fIya%5Rr#~fHH=FfOJ5Ed6jg_;(dmI zKwoxORCScsb0M|0w=p)eG$M6$vo#_$ay5I^be%6vHgkXb{!P$}Lns#%^v8BPxcHqU zf}yjW2s+95O>5Z&zHb_8G?Z_<&*@{k?nc{eZgF&S-^1sc+ho`Fso)v)ZHh*fm)|`~ zRq_g?Gakp*yR<cvu(q{emL*wuJ!RI<u(h<ku&+KPzboI*54m@L@b`S`qAD-Gw83cM zqk0JaZ1rH3b;kZFeuiXx0otL}%E<Al8OT$9uyCt_dyDfZu5mUe;r?)Nvl8~EMaxYF zn~Q6!Pqu%n*5~lWzApyn>5h6a(SEIV^V)sKiV}KL+%7a-8LQP~Yh$XuB)q=VOZJ0x zIgilLir*Z6nZRd(DH2!#Ub5%Q&1U|AwZR9^oJ&uhlb7QqzC{o1rj%Ut@$0(68+!io zhF@@QUO{ZFao%w=_n%Q)Ju=*Js8{SnH#~B`2GlGnj?1ttIi?+a2&2U2%F+~#-`r_Q zQwk~}?238cI)ODzVZ%)2_H}uv>AW)jlGlYdN~7>%^?QTDn;1$liBpo7sFQRV$MOVT z$Ya_4iJ0`a+G=!NEIR@}!UA$0w8gLBPake>7)LGM`Cq|1nE12ITObtgmDV*!p2zGC zMZ_Hx&wM+shlfKSEu(v9#Ehmfz*dW?B%(kb`TbGq-9BpsF11AMfN`9>z4hDtFo6`N z<TQ`mfdea0n26%Mgye8vj3$;(#Qm@F!b~*>`r2vb+H3B8%O7#d8W!_}k9o%Q>a~UD zGnv&p+U`fC7AKie{K4T!oYTL8BKdm~V}fOj)56mhLHQv@w92JP>h*dKpZJSQ8t2ES zy6`Qm=iK};nyi*QqxfXGW5=oU)0{6aSw@;;=ZEVT-4~`7P8u#KEU25u@AFS|M%CR% zlX;WQ39cwCkd!>r$+=P$NAd7tzV$V@!%&%?rT$F&@O8EDBK0Ta&=yTemAk$3dHB~3 z`)`ve{Ps8E+pU}QvF93YF*id%*`*f>hYp3Cn4u>w8-c!gXE<%9;P=AILBx}ZW^EHC z;q)f;JU`v5M;zpJOlI);<a_sV?H1%Ag4>t0S=#eTR)?Tx-}=^wsT<U(8*`}ZYgju( zGbc$^!r?WIx2U>Q+0SkeNgr^s^%;_tFqY<BY;)7eswLy#RXM})Vu8c6MvgeY2@cY* z$;=t4Hf_-vSt1A(D>IubxUe@iE1s|9>3nqBesV4iXtiWkk2=itkZBn=N%VA?iWlwG za$$xAq#7JKexIMo`bEi3<8-)ykvPVjGI`{i&wa%{=NZ6VLxk{wlIbVaqS|^Rv{r<f zpjRH7(3DN~qYcjn`#b9HAJbQ+(RBv!MPm)uh4%NRak#2g(lBmFAvySgz;@sU=DhPU zb~>ek&U9kT73=8%41N4Y-$<AW*7Xk!n<NWa_T!>$oN_(IP`@f#3U{)B3r@bUH#7!m zUH+DvMxxtwjx1)u<Hvck@)}?YCm4VExr(Jc$1vAY_t_oaC79ZU?ft+jjIId;BBj38 zScTL=$)QBRoubpk$$Pr%bap3UbF=e!<GnnqubG3(3zSAGZt^XdO7kVfpKd&2t9}-6 zp8T?8iS=jaIU~MbW|-5X7I!J`y=TkbEfpQ;bcpFs<gnKeKPC?H@e?;y!uQLghxT)x zG|BWQle6Q&w1~Q6)s7eluYo;iv0zR${h$+)I)=HET;m&L8L@7hd2yLmFk5MQH_p5? zhpO5&%M{C<IEgDamXtg-4E#3SLO2jvN?<KujyYKE<=BBItCj7_OF^SxD%~HS<B|A< zXK8TTLOl)lhec&kst`=JT+cz?R0jzFFMVzq;4qNpF2?F^Vw^oTf7q*f!_|f87f%qQ zt8jklvT_*LR!bi3Zy5Tm(4xLPW&je4g;RdR>Okx`458UhcJ}8ub|Fz6KMIR~S<as6 zAX3m#i^S9JCKUMUwp9qZ#x=6k=c#lyv_ly0#*~;%JWY5dGkSrHzGfKun7IQ2>>07l z8xY}U>K0uvE8!$j>3>OKlCWyr3ybYxm0*OKH<**A3eR03{8nV9ooQMeb5KFm$);VC z4h6YI3)(o4X?)g$Fu|MIb$VSM(FF%7a!9SLyE@m$8-*Fdb!NuVEt{efuit(pus|;B zD;mVYrNxlK0ogQr9tsRjUuozblpr#v=Go~xLB}t}e3K^AN-(0oO3DyY-y31MTS+^A zOB8{?8H)-Q&XHrgz#OGq6ZpFGD<N}9YuOhq!$FY{jXU<k{@n9`kKZT%!FG2482QV) zj4qFd#E>^2Y;;qWqNg}G)y6A_m|rnc(pXS9ipQDMRQD*8_58eQvp$CzfFrKu?Mgq6 zX*S&ewY^vzk&7Id@zf7in+O{8q0T=<Ai@NT9YbC~MNu?+T`+PVlhQ+T#6KAKReDH( zn-{6uXSfTQ_GQ#t)W9<OoA_{(;tXIC%cpHfBHv<|lq$hM4){$+pWHX3rPDY-_UW?G zZ8MT7I-dn{p@NxR+~H$VZ{DL^9V%w&P0OY)b}b?kf@PCD-C)aAyb7ps4QDgU8UT0} z0xiUe%u@zs{Hp~f={grG)_8iy<@YsnatxBBM8k+B*J9GSKFCK^YncKp7(ZgWGOT3p z%I$-KNC>jQNeyd>G#HTVxMn}lX`qT3t@3+Jez;K7)DA4V1(Z>?nknB7cpP$4J^+!O zs`wD@BR!536(@W5L>P}&xgVRglMkz|zjDkf+9LFfY^KFkEi)c%u<QG~fDUwyj{`(4 z!mWx_lb$?yMZ4d`3543rpkYN*F0x!ZVtzq7CMW=MAwxSAU5GEy5Eog7KSr$IzN`6) zoGhql?L^*71UXaA<vGyY0iFj)>K%SpZrw#-6?IyY{QeN6?UAD6jatr;_jr88hOT>n z%i_CV?5T1xp2U>ag4I4qvzkP8eRsuwOZfG@MJ_hrH2Ie`7(#>=#-~gBQjPMH`<j^7 z90Sh#H(kCtTr1hpq_&OfazlP1;d<k)wp2rk_xx19q=#@ED6Xma<Oq@DWu34gJSMo% zF(={b&|4+uCfc~vPJM_dv1Bkyi0U+ANs1EQk~+QJlUQI#JUQ|p8P9TMPo8XoUJlG~ zMy{7LmBfh$qsWICQ7|__!uA0i`z!f+t70-(<tB7bkf*XB38Gwz6r(i2P7se&?6={= zOx;1nZUNSKCKFc`tl&U<(cXq&`Gj!o>6(%ZarE}xaxk8d>UKay5wR&cb{~<HqvttY ztnEfvw1y$mL`PsjV`y=o1`&D`L}b=u3m*@1q_o5t#1lc+BABEWU|`fp!IbRCh@r2w zVFxSvX~#0S2BeeqK(@NZdKj!lB~gKyAc#z-0Sxsq#~HxM2sZD>ln4>iftosB?a@}F zmd5un{Qz4r73BI(J>(3rP~zQ%Si#DKkutW}B+nq<Pd?=5yea-rUX$={+Q)9H$6;DW z#TGKgJrTyfptTN}!)1HK!#7xFb|(`UV1n8RUD-f&+Gy-qnY=|Z<*o^G0#_DaJq<Iv zLqBP>?h4Bx@ijmFXj~)Tcup_qak+GBh=QzQt~bm-i9#@O_*6|HS{q#V`s{K9pqcw9 zGUY~}@Djgte-p$8B<}`@EoE6$FEA43G$G!~pQ5&Z6vWkB;UrpH@Qtf;VQ!3Yv4LDK zc-r?wtzwJ~M|M)%pc)fYT4-Xh+t80%;?3v*+bamxePW&bq=zj2s}s>bECnstF~^86 zR>ziaQ}hdsA3i>gDa;vw&*UB}K11ccmGlDzCOo*MBw^dYnvh0t6Q!M`&%TA9nioI1 zBd7lYl)3k_Z0wtU85M?t9s!0$W?gle<cu#-33^g;XI?pya8P~cxv>zhh+I9DwCUcO z)zGmWmfVF!KKAH35Mi~UB(ST32(pfWSSb`v2{AJBg!sr?-!a&+#qs=O4g~CYbRvCO zfbjcRxjwI;-iqE-hH#FbF_1*S37<2dR(2GnzD=51URX*Ch&DK~21+2;Pg*1nt#QGh z7LK;&Dxijbylj7%AKn*iGqmN&Llsi_QY6{Aa#AEKy#+N#W0_V+5pkU3EDoI!G&<)9 zrBtK#zNm953%gKgWL@MV2`Q{b<>QxyQK>w)x8jHt=TrvT<AFh4E91?MmLlca0x;Cp z#FDreT-sb_YeFQJ_>F>i#3X0rykYC-69GxyX5F$d0d0Ki?*W}F#Ah*(Yw4C7;(*R{ zF-&(D3JlbiwqHLK>5+p|I%j{Zz_uKP#Yz;^WRY=0xcPpGTi2Rt5rk-;fKlo%xBh-M z9Sy{`#@B>AhDfQtg-3b2CT0yQ@WaP-7ZL(-EH2<vW+GbmM<$6_L==F?DU0qW9|Gh6 zt-Q%SX7^C9tYC*#H&aA&w8D;g2+|7NOHN$lm))Na0rTA%R_3T~!_IPblQlDS!UFR6 zA-L*hIHeysDBx%EW;$NhNW{%{$Z%Bz*4w7C*Qa>DCP|%jyFrdNYEswYqsWs49`59T zkr>`Q=~H<@7~Z)_-gh*dpWSQ*OXEf<N_clM&G~=hiadC)y)l8@#%*%Tx?R7GB%od3 zp)FuFsC#wZ6}#8FSJv{CFdqpzzz+5uYH5V^PeKD>2nh2Eju%JNxDII3x3jq0)1Ni6 z_-|f#i73R>`8D!`rbeW)BIdi0b>DyOGc6r;;^+7vLLoX<Y1442Xcvto8vH!x<;sk1 zSkR*;2An(Y6M`oRT!LPL8zy`#^mcVbn~@_wJ=xLFHRK%9d3a|GjHKz#BASE_$dJ-B zmB=8>4{V+dRuao)HKP$=EAoROz!1k3-^EfF8p6CpD)CV^W65Sp^M_=&H*hO^n44~& z(-zp;a7xKlgQ@|BE<h@6=eCg~a<WRAmjEdvv42cD?nNbKp;tI1YA{+0f?SQ$zz0X7 zWkHUO7QRjSBSxp^Swwqo>X4v*!%@-!B>M~VWfQF(u6zg)L%z0$;7^9bKQDbKXGK_` zg!aC8!$A9Fek3#RRLO|9f=l^HnOvJQ0sIA~-waZKje5+^@;juGFb&A%C($6rdFLbJ z*YwDnSZRtNfDNEYP;TDyfKD1jH3B5>>{&*UdYiU5&J!Di!sC92yXShetXu2?=<{<l z_|2Kj1vbA=kUhm$+nQTQz&9gJjZFVeuJ$f%^Gx1L*Cw0%5^eUUR#BGBSCublpNB4i zKH?&!cY`X6U+}*{<An8u1ARM%+o=T#(KvB#PDr%;I2Mc;E2X6(h0w16&;o}X*x|wL zSI4zurmpRaVI+=b4sgNezht%FCRSRA0JZ$S!$#Th?v{i0a}d@J+(yp;d*R{k{M2CD zD*l9#UHKud?SsYui8~`BSlrX#PPiAjT><za+0UBs?KQ5&{b|3f5=1m3aD`HjSZ4?^ z^`TJE#*eMlD$}{{z5<CsT$Gd5xUo7wGg!5z!mm55vP5%e{O|;VeU4O*7h5(-q)<_L z;$bAaDT8z9{KpT)O<sCcr(3OUX@}xW&kaE*HsDWw@EJzLg(!0T&PXHYiT&nbGG*4^ z3-RBQFehWH;s-5Iy|f3ItvVD<@K}!Jz!Z*oeb{_hoPGady7nbK>0yq#Btp2G32>z8 zkE)#=0uTIxUYt$xvO<$~@exw&qZm@L?pF}UTf&qu$&79Syut=WmEP5_-qFD^9W$It zNbfb=o6VBg#&x{z=bv23$(N+<0%>m7;D^XhqkMPa1Ld%LP@e+gim(7b`RyT}&JDh# zn2=Q0s$8KWFN)uZyZjWe)_Y^KcWx)_Lu=R3w@6fO`K2yp;-Dk)0siOJ%gi=ok2ex( z8`rae>+SFvNmrLm<O?n_E~;i$=&AcG{^dKiFSw8WpP$>zGDthfggfsu<4MfhX*vYl zv}eew<$?0L@55PL)1Nl<L$A5^dw=xF;!MPwnULcK?e~jO`UQ^B2g0$Z6bj@#gupc1 z^cL|;?~-VfaOJBAlyq&B&o6ma8w#BF5Kc*X_Lq4z2u;)i9jx6w$V5I3An;^?1jOk+ zYj5?a>o+ppjr}zB3Sd>Gv6@SJsTz&Q!7aoJ2t7g?0KDNBc9r&pmaJ747!H!eFM!E= zz)QZ7C<B@KW}3y~J|o&H@4XaP4yV7Cef-UYh2<oLh5xSfyw-P8J-_lvbb$$f>L}!j z(x5oY+4aeNr~4AhX^s^tM>dI2%AKfr50u9LhKVEIQM<a@t3TE8Q%wa{MG)-~!p_M~ z-tp_3mjq1pZdcpID3=>zmlKH0PkPByY$&FUR(Ub-Oeq>z!sT$nU}%51!6D}Dx75Iu zGp8sBos)Im!I#DKY?WaRB)Bi#)k7ewb>#Iu>4nv1SwbU9HA?>X#4LM$-BFq<yj~lc zj%uq(pIBnj-bcO(`-!5=ofsz-;Tp=BXU@Has1~I(qLEtrA)@xUUkA7UfOb$bpDX+= zb|o}CIp(CC7?6FvW<N(&V#2GlS77s=(w=&g#{a7Uq>^d0mcT8^<MA|&l#Hw7SLm^) zbxslruqlq|G||q(tX=YZ@F`xy_`<U}Ao1>P%ks3oICzEK)^2G!mT%P_7T1;8P{BEu zR60`*VtI5o#UA`ZKUTecH9g<E2Z+urljmsfAOorA96F`D1oL=2+(;F-kvuTiyO2Ke z61*1V2+dv#Z>rK#+y*vQ40?t(`bG?{R<^H&IS2?|0asf+0}CTZQhg&6GiyHblcqLu zQZqw7auqgdkhHC^k*S%4yS<TuyNsfNyM+OlA-MoQ0<SCgE5K`oOpnyn%F^0_+m(;} z4_xlo_rJxA<fMP7I9l+Lt4hm}3ftHlk+L$dGJxnsUCo@C$@vjTdF>62x#dN~{(^Y@ z#7A!G=xEE$$mrtY!r;QfU}JB>$i&6P#Ry_%WM-y+)u4B9vv$;TrMGsV_zm$VhKP}a zfxVfnqnV90>2FLueH$l7K63KccGAE7v$B<z{ujKp!(S}C^1<k;XUoXM0AjSVV*ICt zgQKYPE686C{a-B{6kqF?jPgbfHcs{iMxxF}){YeagfKMtm%Xi%z2zU_7#c7dSsGcr zsye*(%Je_F6ql5i`<KOU3QWwbZ2z!&CHsG9I+_{(o2>ug+wYb?!uh8oujc>4{U6$Y z$Nq=%tCqAhw}_2_({J}AMfk{n_s?x;V_;^;{pVW)W-gGi0W-a^AqN{ht1*a+o{J5{ zN^iiSr_aV>qzB?K()$OLq_u;ip0$C|Z>U#r2D4WjBX%PLJ$(*NdOa>Cc6wGePELA# zE>3-V4kH6YRxXect1&0@KOki7&0e!o&+?yM{f07pg#xh|vFaHxf#}&;^jPRwL7dF= zoFEP+dSiAyeP&j6Lt_p@gFm1Q4Y<W@?5*@(htteT&%}t)*4pHchTnv93(86Ikux)Z z{w<NS)N?d`HQ*zc)-xcLRrq&{qM4PEf}`GVI+@tHKwKQJK`=8jv$J#k>GU6f%0~7M zuc`POlnKPZ%=(A<?{VRNjpmhEz2AxY3h;;gH5hJTdm}wZ8+%0?8%sX&-w>p~E&s@G zQr<sDMZ(PCRm1If%Kw%03PyH+e)@9`SepIOL`wQc-g4_1{Mm_vp0knRA055g{n=z- zs%LFt^g6-+%Ba8PX8)Jb;$%1A)H5(*qc;W_>Cv+?y-q+*eS_D*W9H=G<OCUmnA!g1 z?O*5)HpY%FdiF+wCa;mcM)R6Me?&t{^(U9q|I-;4Q={KRftXq7L7em;W<@4eZf16F z5Hl@^nHvNmXZ$N-#^3Y$?~r*J|39Sg{-N-Xh2YihPuc4N^SWL!{%gVdi?rW7{y+Tt zYcT#F_V5b*-%kEl{Qj4&|I+opV&H$J{NL>QFJ1pD2L4yd|IM!d-{?a4_m#)U`t=y( z@_M0}iB?vAy$r$Ve-syikcaSwc<C3|VSSar+e)Z8KtNzq{(eD9%D+8*6~a17N{hno zz(FD5Lm^D*zSh=AAtXfv6<y~KmrOomO<kX!t)xv%cu>6cjYg9cI%&CC76e0YWo3|u zhLUHo5-68ZX=9@0W?#OOK!+1;yaZGE28_M=mKu}FJc_tJ_0qy#!KXcaGkgagNjhvX zK4EiEHoh*HZ#f_?JQ;UN%S40SMZ7eBD=1j)S9@1VYG`3ACWjKsm=K$;NL1re3kce> z)oSuOyVq;@>YTk7ktRo|NT^K+Y3=Re^PoKzbLnyEI_7fFbX6~?)BgbhO$d%G;Lm^S zsG0M>BAqir6~EFzO=;K8%=_z=>(yLiCdwubCP#tWqkXg>7syV*t7GlftHhgShxkz9 zgCx@2_E?HXK8+Ix<ugv6iOc?FwijzD2j`#3&VB^6ZZ!(s7M=Q|tE{aTeB59(i_W+9 za=QSzrrAe1%R=x)u#OuB|H}iRFKw_>U(^+WM_gT;rO3BOr$oCPC|Sv3S|Q1RqG=B& zg98GmmJHG7jK?auJS)u<P5|@}+#<d!1_Q&7*(Z5y%r=eYhF!!lmkNKPH>!UJCIolZ zCODOe;?%A=sJZkRUa|I>ZF_RR<e+Y@?t$(^@G;_c7+!i+XaTVdF<tPN-#X<_9-ohz z>))W|{_Wah)Oinq%^1Q1mNp$Ofa$%ydiVU=fIzuo>p2~!745RxX9Vh4_<wczrAZhP zi0Sjo<GV`Pj77#svBUnSK7qJ4zBZ__Z+xH0?B4vFco!V{NxY)YX~DJbPF4>>S7zQF zKDV~6pkDbH)zfpT;AE5zXEt>#@TuEX3A1lP8R5S0n0796rMKWHh6E|(<cxXoJd`@c z6j<ryS;<O;VDkQC<tD$n(9#kd?*>F~k6Cu(^bJ`KwgV(>m&3huJiHG0mlsiNI@R`y zsfs=ja>;XiQoP=((!ha6xwr4Nue*3e0_X{TM|z72c0Ri(Y9C%bV>=tmPdx{_XwF9{ z^V!cijx16YBw%iC77BA<Ht0AA&`m6q9#2bHdiNWg$E0SlB9Z<~H(ftnZw2LE-MNBf z%*IBz2#76(rt}M>U5uo4{}5Tl2dA`B*J7;=kNy;~g4+s>l2yDzW>IKSWPy74v6_Ph zDn7i0YyJ~9!?CT+)o0{;_fXsCNIo{bPUs-fdCRW+)v4Zvl|h&~rs;$d<o1frhUr`K z4BK@m7ZMdj6_yGWyTfV@zfK&)G<r*@W9l@sv?$2;BP+F@(E%_;*6j}F+?+ba4$vf2 z+DUQ>-Uy*EuvokPf>-0cXT++>n%?rYunHS7C&lr)$;}j>bfE5!2{u$Ab_v^-JgHuz z{M`GwtuV6?>9u-=r<09is)LJ-zx}+5+%(rkOm)la?I{S-v^SzOmi8^NsopmBL_X%= zW8wSg?ZML=?9O(^jU-Jkeb2FssuzFCz2@1W%WjC@tT%kf7FDVS*m<@~L#$dD<1S<` z;!pHQ`j~V9w#ng`&=5XbU5^TFgY-l`m^-Cq#}oo}tZL1Dyy1rKx1rooE`TTV^d|`9 zMF^S+r$C<RSe{$%SjLNV;@S2*MDII{yZe>PopF<qzq}*M`L(4N%>*QLuvBVQnniZ_ zV~i+sM$e^<FXno)3lc?)_?zDEZ2rQ-RbDk)T1sEs&eh>Gdxe~_qMfwWB&0?;oNC+* z6@$?Ayhj4f_xSkUZ6zQ8hwdh^$FID6EkhaW^8)^|iWO;Q`c|wNr=vGr`%FHhR4P_H z-YK^t0<*W#<Kx58voU2L^TdY@sR<;wX+Lq;2#7W!uV*h`U2cmJW*ykltoV{EAiYv? zi4v4T(A~E?=3Am0o>dhDKL@`A7y&?@Xirk!P3y|BO`e1hB!$61;a>DhXFJf{qHow5 zcf*Yk!S5s2Rd2h}D0?{Bs{2qe9Lwen)s(gl#19=-^KHq1$6na7w=k~k??k0xspiUF z7eYJOp$qzQ4KZPR(<+^FQHc;qhPYJJ{$X0yA~cl#E?T6*F<f)(>1I^}T*r#~m!c&z zQ`=_2;QOZzkR(Ai-{r!>C*#j}>&rpAvLC!v-Fb0|*(cd2{SimgUe+IHY%kPXS=zoH zmY;}BMI@8VLEX#jKj@{?kFivmm`6qGY8$#Oj$f)+w);Q=pbcS$E*TMtz7v`15EPx1 zv1h6CQL)qw@z`l)I=WwK{8Gq#jP8aBiOZO(RE|(T@|$EX1jOh7odmzjJv;}Xv=wQ8 z!sz%sXUVbS&(*HkqlqA8BGoGuW61k^Ra+l1+jiJ~jt8A0*(SK;FLs2e8Fk^7B7}3I z*-zhSQ!V_|=b(p1zT%YjbqW`UT(dO9M-yTjfr4!QAUtM15qXdH5^hxbsbfRSAN4kg zv?ww&Q2C|aWvoK#A($NvN(C5+f(y*k+t$2D0Gr(ZLWK|={>DLad#nK!Eo(&w#<?%7 z@2KfaHED(Sr8)iK1Fw54^^mZ(v9ioRMQ;1XKV#K+rs%$YHIcwg>IP<1cpjf0>Il;T z9u}+f9X>!tUJYtKhINd`Z>J);Pn+rx-|S{nNbh^u-93DU`|9Sm=ie)FJ!7n#kS}pj z!alrvrgjcaN9J*GFOe6Hv&-_+YFbh>Ry0<O7NBN1C@qK~TCwu6=-h#{Ksefa>^&HX zDHbMucv+!YICH&Y(w`bAm;d1?Q9UA4=!`EdXH7Sva=GFdR$hV(NX{M<9FQJ3%fjTZ zPvE2cQAvf3^yPz>SSud$G2g=Bp#;^T!YO!7P>zu$35S%E&s9>6t3bVJU~2%oAVz^F zTNyp}M*1+}7D2Tq<%C6LW4biWc+A>7zt$l7ROyG-89nJ9Nk|vQ3$P>G9~&6yH~H`l z=PikEHw`>820x&jqHff(l*~+Iy*k7pQ!~HnX^_}IdaDB!dL_VoO$kuM3yjOlm-(m# zTUTCcql!UhtJU5B0Z{bFH+PPMDEM4CKKY@TY?a7c;Kcx}Hh=~@TcHO~NHj|^V~<tC zxG`O%D{w35Dci@%97y~ys>}_GeiBrVoQ|L_J0)5RmG12>aJckFKlCGN7&UMj{4tdh zra978>zo@Eo@G7ir;303n9narBJb_)(@<xj2W<0)X>+*)8t6NiI23#TovNMY_ZX4E zgyrs^;c`5`JCoBx*uT?syBm0~8&Q>7mm@qp$fjNR3kW3)g$=_Cz-7{hxBx4>S0H~I zPCo<k!ktvE<(?r#m?A0R&KSUjqsM9#$iXY1H_ukT^M?t<5Tm+fJ-fc*hbcVC$NfeN zj$@0<rjZSb#_LeGg((o5+%Q4NSEr^IaS#q~aXi??$(UYFCa_5L!B;6tFWUxw-Jp;W zc|?pYKbJR7dj7p#LkLezPlXn15px{hKX3?HmjIrUk0+Pfvc|6;OW6is9M2hReu7*0 zme=aOX1Lq?s4q4`S-?aKIp{+IQryrvgJ5+?ldD{PQlQ_!BW2O{wyzAL<*Bt#$aybJ zKwUSl6>K^BX{Srs+hm)B)K3#%oLwaCXu&iKHP^YbLCP1FDaSSEEB0H=dw;IGcA0Ab zHnRjKeK>>c!t2660-grAaeKfB+C8m@{p4zAE3-RdpI526PAs;H(Z8bJ6iE84osL{0 zmwgIzguzgSbBT710kiK=RE=0LDC&v4t5Q*blA){+HP|j(6!rl&q00jnVJK#4js~eF zXpbZYrJ-W|8zkfFx-r89n`o05CHS)%>p}s!-RyXWUxK#!;+NLos%kpMXqqV^4uj@t z+ZeGtzm!bb7<P?xP-zgot<%NSFO_0HkBA4!8w69G{3pbtD$+~^p$|f&B8TC#Z<tWm zHSH2Mum$&aKC~#C^QY2!Jcl@ND>i+iZ!<@UeQL0jOIph?FEB5{S2ZX}S6lJj9r;C{ zHQ4ndZAX9P2dTM^qOl=O&3)chRXh>t2*>sCY(5z?OP08Z0aXmiOwLd*ni@yZr%e>j znH|gVALW7s*0VO8NJPU#=RBX0&o0&t1|x%RPjAW2WWKBr2=EYBT-}6LBm4P90QnHP z1t;oLaygCwMtMK6eopz=cWiwdl=f+uvcocf1rI$YqJC^5%X8m*$Am%pGf7{D3)v{L zm$I7=C3hx{A8!dx|3ZJE`!YCuD15V;d)Kacz|fSV+{!csMs6!&U^(s{JemC|KdVk~ z59i~K_)K6xe+bDtO`t5J>^FX7OM=qjlKhP6DfN7dR(N%+(jsNN8X|R%Qoh(o1|sYy zR!ni6muy#b!(@hgO}hJ&r(?EX>|@G!`_}~oisAramkpQ=ch1(e?qrOWc9n9QXXinh zhTB<6k7=cb=;WlBJG@v8>=kk^(nMyd8EKWZ`^N*sa)8^$c`ApBaB;o5*SJ2GB1-Y4 zT&^=(EekX0IDD?(holc%;Pupid3cVFOhO%=)<D_?d;TBHM4xqT?xuGPc0sLO$=B~Q z9|uE_-xy>#+By2~;*dRG*w3A{>JsZc52V8PNcZIRVD=$3-Jbnqi#8gpGu{Cop_*e& z*MOfeu82!#(6Yotw?v2n3T6u`s9@j5er$+dg`7b)vD&e1Xnr}bN_Fv-kIgID1pMC2 ztaHp-=P`wG$COXX>mRO2YL?5{3z+1tq&32g-kaJvW@4}Aam$c?5tjhUL`xH`lD4Qp zvg$@ZbJ`|eJYw|qgu*s-YcFPG!mJ}Ik=$_WG5KCe)+$B(c#14cGEYv1sgs$uU1$O` zx)=IV3Y2_0Z#)oq*4h(zehIE;#g>b}%pgE5fYkNqs~cx0p6uPHxLPI>;h(sq_mb0O zc+ObHy(FIsb;uYvLosP*2?=$Cbcz#sLLB3PIs%{gk_2WzO({Ntr}Xl?U?vsroslx$ z6@;V)WmUpwQ$dVNdz>ZsGQ7sf@qsc<u|m2oq6Q^6gTdDWY9%mMt^aF31~|n;)kqb} zalC1tsH1W+lgAnCPjF>$up`tCc)oglri*Q-mSrYXcT}P2{joNz+sygMRub9eaD_@I zvHv`lIiJb_b84EG`->TdW>NZ;;dEHLa$N=Ryz|_AXUYe#gUZ0$fU0@ZSoXtMm``}g z-3jtQXwY_$BKl%A&ZC?cD9+E#eX<L0%fGvSaESUfUcw*;%Urtn%4t|T17VWQ9_*pj zlBo?WO`DdpfDUpBDn~k48!8I_h$m{B*>iV{vkbL;z;YFsAuZ2XoY2uQTQh$N+_dgr za@auZYff@I(l=W5WYhA5CSwueM@WMc{UN%Yr72M!yS#s?uxhqexK;?;s`1Ew>Vc}S zDD{1u1ROxT{EipVFBD(`EfWOg(Q)RdQ1Mdf`E=8Qkbtj)pTGtTcM6vdk08wy^DAB) zv}N0!@0zh+Q*?Aszf8(lnY_4!vbAxrv9}5GA4EmwVMQKkb7(#7d=O|!@r-=1yh(!C zeyiZS_imSkm&BdlZ-PkXhARezIHtE3<7XR5eVPjYO~~RF=%%~5eA^qYTEpuN_YMM~ z3vsz4Ijp&uvW>!#C)E<?{lzZUs*F>Dy?ECzlc)%RI=rgd!*xpmkf*V>t~Oll!Q@DZ zC&Z)2ZTeL@>XewGhUV29=xewV5>*o3Cq6AcKGJMyv6N~9(vuHq)Qp%V+R6e4b1%oT ziwl(|25ml}I0_`vL<pQeI;3x}XX_W*oVw0{z0Xd(03;yH(yCu_jD=$9S6E*F)FANl zy9w>r{jH%G(RgtXy%A!iB9gD|*}o2MdEM)a>w;t3+8bRX{0B(Jx=Lg@sXUg|O@tI5 z2~W%vUkZ0vHat#QdKv)rq9N@my~i%XrhEcE+E>e2eOGkL2e*i07dcQ$dv!QW_F%(6 zL+PeKN^Em-Lv4fi);DjG+bETtwlKr`Lzw2fAXJFJup-@AgH-)RxP7-Y-Af{oXK4ja zoXp#`RY7-#0+q8&TGx2yfVXl}N&TW@8-;oAAk`9?{Pu;FrVu7M?lE$FTEA(h0-o?9 zD~Wjh3wteX`QF?XXd@<?Ay6v7G`?PDR$M!6Kk*cQ_s9M;%)?eMIEuyh@x~NVX<wO1 zWf^6^xv_u&th`lnKi5L~o;LqEZVPRityxl)eOGpgn}H|Ih0yWP&xa2(A8_qQF~GF= z3mLTBh{a@Rc$!Ui)A?g$haue?*S>K(cAya1U9yAjCFIfVF1;fy!b56>5cmT5d|L`( zQA0nCjeP9g0NWVc*@MGTx6nY$?p?PPR%+9p6eT;#S&cDC!8?$Rrp{O+wW|b5thETD zSh-OU$W8w>+P)lht#*^!J`ZV;Qd*4Sk<~hPJM59MaFybIc5~v=a`JiMj-kYRb#N1T zuEOptuQElb-C@9U*7FUoKQHQ@uwK5=%!n4stg2m%-GpSr^o{$?L73pMeDx&VY0@6w zPY)zR=aKN?RH%<lbYU9&h}XcQwgv&r*%F3=3pvfYTd7JMkeat-I)paKLXZb?a5Fl` z%_1-4`<?HPtBO`*gS(aPybU~!1WhUJ%fN@aV-F|>zVAx!4ot8&I2f44@I3%=g<u*` zx*HutFH<j`c+!hk+cPD>9CT!<D5esuhj=efWD6KFp+8`3rz;J?0*XdOOJQbJv;7W# zXQ5+TIi<;l8d;~2qq(Y*lJg>UZiEoT!BNjr@4FkB>nzR&{UYi$`cMu#mC$+_SY0~G zXLk%#<#Ad1=47CI<YCcNRt^4yBd#ERLBdMs0|7<KK<8{>Newc@XCRRA-w4e5DT>(f z&G5&}ICeOP^qCld^M+GP1~!pBu`a1jIF`lUU6D5F=Jh1ly)^Vi4AKdbNWijQ$-IeD zU8lbqjP9_9%GtNgq{~>&f_z#Lg5!H`{<hdBLUj(PXzbA+XHq(gIGMpa2Y$D3MGfXM zKSe?lLyF|tRU^Z89Gj63PNBpJv!bPr#fryl5C7bzj0fI$p^80XbR+60r5&mRaKEdA zBp$1ee|#L|F-<?3?!ua6M!^6EM2}L+4JKtrLGkit6hU5b^`MQFSErYQiE?S~R^c<g zbY(PFD9j1+i5WD5iQY!sbX0$g&_x?dse*p=ut9v?JzYdjq)ZwGucHT-7)M{%_5-m4 zNw`If3Kre>oV46+jz>Py^z1m+%P99wo<3gCtkMhlEwJX4gQ_rV2A$$TQBXG)$E-Jm zAjffci<XYS>PQyg;;8$vg9pHFKd+Ib58|6wqgFgbSh^_W)t)G$ZLM>uQ>l-i3&J*L z9e_{1O{E-g*M*pMf$IPK)-4=nV&R>4j6gx!K#AC6M!Iif6ftzdfhNkp98y~D{fDQf zjUDF<TK~dlf4?~+l^|L`s0o*V(+GrBv%88k(3mMA$`@i_H>~|E+Q8QUYIoE<&qK`b zfPRK)My<Ww4hig3RA`P?OUVqf2e5ip?4`bQ>L|{ffo4R$EOU=f12kv27mCQmOlp`5 zj0y5diMGSqFg))jr4??X2JX-BBK7f49#@_~JZT>ry(=Kw_hQ2px`&NIMhPkzJDjN! z_93)yfC{=+r>BSfwjY0@`HT$mwpKGt1LB|<dVN!a6Z77H%O=_FN*dIpB2=!(K81E| zo$TH7Xg^ELZZm+!T+vaiE+9xO>2chkt|+NXtHk#CQDYSB2DxiQtxt@Zvw1V(4YjD_ zs_fe8)2Y~$@I*LEFfTrP(o)O%9hiZjR0z|9HnGuz&`y{}f!Az4S2kNFXz3cxwvDi= z%URk{faDtlrYh3hq^k0FR%$BIig$ZG(+e#Qvrcae9{2<iJ(Ry>EOk=x`;|~LFy458 z8Q2<N8-x(~A%uQWqjuZI^tUUcTtG&b$aX`DeC>74V0P04u0oiB;R-hemD6Ykk6^DQ zdhB5nx(_fCyA5sY9c%5`7^!0^6EK^v6ENG&p;~j+sPFDvRN~zdn!Q}~)ilq!zH7*$ zv-vjV9l*OdjP<ev+PR?JWsVGr_)_1f6DVQV5Jpx&UJ74Q02<mK6d_tt8fDWVkg}^< zkuFEMM0DivL+&-(5h-;1mPvS!ec%q8p|>$+?k#A2K8-l4c?iGGRvU)I;j^Ke&j?N9 z&Wy_~nCwc6CG4nBGr$?M87@#z@_IxrnVUMWrPI|Gh8vaZ#@E5=%x_&Z!)B~)lB2V% zTdUNWKIjLe1LNod%*R27hn@4Xile=wi8>M=Eh3lJszIc(yRxIQR2^%TXYsp(ix%H9 zcas*{#7=3+JdrPpM05P>yomy=d?x#dnV-mTgkAjAhwbD24g5WGJ%PJUmp$$WB$gFl z$O*wFEfLGlD$v_BDnAf7?Vb<SGiA>0pokrLeE>5rcmBFW<_fTHOyX?fIAK2{@hhii z+a6ukE?RJ`UU;V0Kdkc}dKtK^9*cto^3umAyf<%h9DFZNyrc41ZH5U-F6eAjdwsdf znJ4$sLriSF<{tH;>sfEA7XcYpN>7R;64HgL>{kY-0(%0(j1W>y;Ur1#1od~iQQxqd zR@8Sa-bV?RGO@u<l_2>RL)}B8_+~{$I*WD*UKv0DNO$GOw%WUN@vO^iP=e)zcbB8d zbRFid=db;iV~8((cr~;qhY{pV>F5}EVfP`4HwEE{-rPr39Iw_+v(GETlIM$J21G%n z>$P5adf!$Q5Lp_r%(~#T+N8tWV`}d>HC!)Wsg#Wd2Szb|P)`Lqq$kgKd9hmZHhp^7 zSDUS5qGu^ZN@dOwNY<9wZuwotbHN$%;i10e)Se>P$Iw0Tzg`oiZ+DPp96JGErW8QL z8Zor`h@rFjoMV1M<}AXWz-$P><FQSe*@JT}=;k~n)Ca!TvnldaI@4Hnm9)u_ZT81_ zc(@Y0@6ugaRozlWDH%&2Z}UjK?^<l^JOs&sMF|C{zC2afUN6z(+_%X(uPN|PiTik# z7<}`T>_2gPL?PE1NRlE>%ugjOG4?mf=8uxj;O_zB>0;h|1&Hi-DN@K)Q!DoR#B^7~ zDAI{fa_Pc(T+w9Yo@Md_T{|a|VLO;NHWeI!S*dY02YT?oH=!u%tS{taGw#lNDWtSi z#V*&PoG0AnBOEYnz9HfM0l{ww-WmEj1=qGW!i_X1*{xZUkA+w}`W9&vkCdsCB){X> zn{?%>K`^h)HUhDx>s=*G0#EtD!KXDWH=nifLOcIh39wOX%&am7G~=Y{jjyq}Leq z>){}1nP?z#G@mz<==~vgUzli|TkQM36XPf(D4gBo{r4rv<DTP@W7rpgFDGWqoQNZy zBgG?O88*5A9l!El96j|&lbSjQ&&f%0W?s3$G}Llaij;Bp6isHPUvsIDFO6&RxNw)5 zWH?$;t%yx8yexR$j(q5&yJ3;-cTBTY|BkiVharwi6ag_xJjy=`373p>E3@h`=BZO3 zYfU0Nvss44BLIi2KtO1HONCp7^drc!;cOtnjAOd}N7Sh3cMrl5x>3$Fbkt*%wCS`u zv|46!!AZy6$%=`-NQ(@&T4PyuMpzglIT+G<W$T$me%rMO#A!qaGFtLM9t$ehpk37c zE;3%yPC_q$`{hAp#=|F5x6+?H^cWqNU+e<xx>02nZLmO5E8gqtA=J8~6|f6kq^9bt zsqHwKqK07)$=*Qv9yWm`I$$!2sd{z4bAd<2ac85fpN&R33xBZD{2v=K3k7!FDHepZ z>HUGgu3NHJs{d$u!(9*sp*dl-kt(;~9DDqS9?VseL*)&~h(;-Bl#Rhbu;^=1baTwl zy<MBm60)vR!W)5kJT$VwqH{-^L!jClCM`j=KaXd3PEyAH4FV2M3DsnK-Ou|PCxS%v zB%5Roy8~sZlW$OSF7?Wm8xI<6hy`L>--PC^+lP^@eH`@cymug}N)$l9mSH(1N$yt! z9}e9)ok1-z%EBbb+F!R;lov8>)i;|Cf#J94NW8L)G=6Cz5kE6T&)Lb^$#ltWV=`$l zFVlu7kt|P)b(GjaBOi)8aoUMn5K#xfIIk1d!_j6*qu$q}%_7)>pkRL>^Ki9r^=>)_ zIt?+6JPP)jzpec086M4vo`zu`qoRhzB$-htaE6Aid56%F!?LZ04^_%6@2RI0O@pgW z!BXXmwJLY%rA0>6Wyu+xFXHhKe$tU%aGEl0DX**hK^PxV`_qpD+8gClDO-UfYpCO$ zMHm-K-Rjm4-UOj_SfdQu2nQ>wFOvzqnR_L9IvoAM(cVA~cz%R<vFWM~gO0hZ2E8+Y zZBExOj>wUqr+B~tYAa+Oeve6bb-PmUQa>Mp>M)A4--Fhu;HbQHslh$qc$7Dc?_;~? zJj==jQUlZ?nYH(-ooDMa&2IJ-#1yP1kJg6fNAShOfN5?!%fv<G&bwWUyVR@I)?*ag z8OZu$hJ#4ho0Q|HG9z2t$IP6K9WqM;Roz)$IBEh-v{ckjvLeorB`7LAT3WVFsEO(+ z%E`pQo0KxQvL8NC$J4T1Q(-CsEsf|hN_=tdMeyBM5mrP5h^=g%@9ricUaz+vpKY|@ zZ>R<2(Z+EqqD5_YmxY&5cl-)xRaY*F-?~UdO0`1N6|&b~8Nq!(kwTDy;nNhziSoUW z{Z8VUiEZiSGKitE@3S$HH7JU_c3Noj`V)GjI<ING_<Ax*UP@A<*YAklI8yJv0coM? zB5QKl=bnZlwidPYx0-Rz%JNL}D7X8T_&1?m`LvrriNVqZ+$O#j<B%$HHDXG()_M{+ zT3bJ19nv4wA}h7OU(Qdpfo^cB?_VOa=ibyDmh>xcX?tEhc#?Zqn%A)KMj!){<q9pf zzi)1TlG~-GQ^19Vl}EkgsX%8aAC??Vg4XPjsr<We1S9Rfnnc#(_%x}omv))AI(NXe z!R10gVCoCRP&{X{=mDO(ikud=+6XNUZVOGk9jltkry`9MS_DIDP{SaY4ShOIog~SH zbAvh@|NB`0<}^C?A8*!%2~;dL=T?348|sSbcMquVZ?JS+Ivp*JP$pP<I(mH3646}Q zY|#^GlEssDAfG}q-^hWTtm85J%xw>*2W@<TbgLlFi7*}7W8@kCa+lUKcFu(#Fx;>X z2iQnEHuJEORI43{nfp#uyz^IxnsN<v`hGi}W5opt60k~<#_p(NsJ0T)z$@ll5Y)}? zQUhFFwVHww-YI|MTOCiRZ~+$)5|s8zZymDtuSdYg`HhE<;agl4Y4syj{77$Ogb*<4 z83!0bfuk^<QLQAC62u0as>@Z*m16$fLtf7QgR2Y&)^H;<Bg-SdbX}DQ3XuUPl*=(K z4}N$T3ET9mea@Sl{D6m#*C~-@tH(!p`@|PdXH%SAr>S~qi33TXT>ppWg=3U}_5AM_ zsXV??4EFGFWQn>UHjZ}$Egd^~qi;{%Wwc}xvAg;k#2K_0u+2uzdLAjYUu5zt=s*>r z^6kRJ|Cr94HqTgIN7jBg@$wS)&}hj>1BUh6k1+!-9U6sQ1b=mCge_xywy1O)<d~+O zPRoF~fbefW8_g0H)y_C*%uDHL?tct&!^Mp><Ef!sP2JSoT!bz2Kh~^AmWqq06ys1$ zuc@oz27CK-e1?b&xemMr=-MY<CvN+u74GbEWSDw?mHk99Hgir%AlQ^eG=buacL6!^ zJ_A|Vzvfv^Q<G3*5l(Y(33jPXKZ2oFieBksJNw{$YAyY(EIjT`I%>~e5KNGyoT{R0 zmo|nyiv#RVByi@<4TB4eoYWaAqI6LWf*ilD!z_wwu%A_wQ@9nIYo5D|n~d;tg<lBJ z#=p9(I^BWJ_G_3SBtAYqu9#^{ZO<y!rqi4S4^<R%k*6-nA@0#FzM`j+V6M*^kySH0 zQYZmWFn7Ms!i-kO611XA<2V-G&v>hN*>;PnnG0<SaCxTAbb?@Jb%moI4%M^x*|R?I zUh%n~wIA@u#a9dDap;RE%)NhKs4|hCUk@CG9Q`zU$1^K;p%GYJ@F7U0_QTkg+KS#Z zRl$Auk@TS07hGc`Hbv<0l6P=b+dmMN7W7v19>Uk(>q=&)0FE?P*J)nA;Okk*l)J_V zWa$#>`rIYHIyEzB%mb(FU+ZthoOsN%jjQ6*8U?t|{Khtb?eCeoLFs`(HHpBExeV=M zG1`=-*7h;;P#n>uNN?59-G`;^`rQ;DwG!U{3Rw=5ph0W}(ow0f26nKp*hG;((N9*3 zoFqIdpD$0|mu2Q)FABijn=AMGxrINU_ON@H%#cLo704+rOUIAXj$MZ0ec(0W#qoLw zs_mbAi(Mi-Wqn0AYZ<<yona^nY6{H1#Uw1>zq*oKbxU4KeZufa14F@Q@XMVP-+)^N z5W`_Ysl?*wwgqwxBdR(cAFxLSBBYHB0L;=W>#)sc2s`Auov19J*(4b%xe+(iAFmp+ zueb8Qyg6Ga5FF*Ju<dJV^fI@+&vZ2C$1=uw3;L`1ck%l;hsj4W$M?MB)A^9pzb(m* z)$vvEB$dFup-NEX{{kdCw#G-7GJ?%mi)O3%x3qgn#sEdO3jOl4CFTe4KjqS0zDf5! zk&)8>0b+cuORsjRBl#c7lA_AK+CECtAdG*z=)w~*Vwnx3vLUqpnD=*Nul2?SGgC}C zoUzIe9a4XC|J(b;qrWbrTGsuT|HMG{v3v7xH8?T|RyxZyTKIqa8h~bdAU3p3HHP?! z3jWJK`e`yVWFh2P?FnUoME?gO2gTZFAM`P*Z+=PtP+l;@l>7S52evri_3rpL_^VSe zpb%C$e$EeRNz{K!Uk~A0A2(<y3K4cyxA0m1r446SKk-GEh6e%}*7-ji8CsKM_e+b4 z5c&@}CrE*0>{sP~vN=RtLK*f<`=82%eKcc;6$ALj0&g<B3j~XJ68G@{5ga}Pif^~- zZDz*giZcU!?P4?AGRfpZsmNdMpNp7Z*Ai$2MuVbX1d6okV^TdGR=rm2B!z{#$2K3> z%`!R_Gnk(OH)@&VB!LPT23fx#)m^64*d9pcU)rj?+o-}pLLIZ?(fX&bU7*sGX<Vd; zT6OjA7w?NJ)UYm{Se&0rd#F6b&)uKJgqvv75T4&}`L{`XWQXKo;=acVBH;E@77qLM z+3-1}ZSSrb5ToZQCMhRhyLw2}h&!N1$J!R>3}(-}OIcqD3jsmo=f@8E5Z)vxjo zRr*A8>CJdz?r2Ov4|dzQrLgX2#UvO<UdRY)y00|15j;=yD0=Gzqa>;%$0V@8jEqda zA4Gy^0pp}oe57K*7!VIHjcRtagB+04`wcsm1l}{~%Dp@vw&aKo7|-bS;CwJrHc@%a zF?N=M0$8vD8YKSGlX}>=N9ScOFdq{tV5!-*V8ij9AzKiyt9*HC^+=BOE_i&qOSE7i z6Qz5Kzq++mi*H_1uH0tMLA8Z*Wu|yGdRPZBoP`D6>c{fP=Z8PzGSV^{ha~xYOTy3e zG}`c!Fw*Vqy%Vl&j<-Zx0@+n=tQ!{0qtnLJeK_TuJ=DE<OViao#6S}U!8=v&A0#1Z z0b>EvawodqhvvB_2TK(+c@7hF6inWu%6MVba7@we&k3}ywAFdHbx(Gx_Ur~lGCEe& zro+~z%oM4xknE3?Ph?-O3D~qgPublTpJ(0r-);nx;0l1knRwxP5POb#C|A4%?~0#3 z^>(Evk3yqg);|i}y_oUeHr32b{#2dY^^LsXnY4aEpVU&|`7v!KiTbVWS$pT=g@1)% z8li?|j5+7$%<#R?O+{I{(;09AZvuZJ%)4#rbN$Z2N~zD+%<(CBP@Cnx*$1GKq>@~S zJ?|UuAy;@FJbtD_sKtb3fz9PixihhS59;9O#^wi$1EP8z&;(TmLx#Z<1%mOm`?k=9 zv1#HYY4bCuDb_FMrk`%7p1t|0qlM{avr0z=bpU)`1sfYa9@f&C9KDTT(&NBHwaSR+ zX5Y<(y@zi$ydqPf^SE;uNK43ANNruvsAagk+yx{7H^@Bb*KbXCYPII81j<N00kUPp zBLjLU2-3}Nfwwx>I>$OUuo%Yd)swx^uIy_m_G_yx^ABt%b-LXRgSW)2gyXuV{*BR- zRCc1BMd@6tQ!MwmHsJ5vPQ+zTOo|_h8+h3ezEH-`zI2FOVAx$;ULySphMbto_lCgy zByz07p^e;yJcXP~wtrSo`nBBe#KQ~A^XAE3U>&CWXAgV8zUUNUu_yABaPfl*58~Cq zl#aFykkx$LRJbcd&OrCdqc|>jzh}S495$mf1#A7-40&Eom{vIAcQQ=2=?p#?oX9MD zMORcTF5m924%sQWC<m?vtfC7pJvZOotMsmw4}`6w-s?Mk;3+!_Qnt{DH2n&Flvj_p z$c>KVfxLiB`+R@>S5j!MA*ejx+&rYmp*}MlO@-DV(Ib=h1Q2xP3!m0^{2u^@KzY9p z1<4g=lE40D=kxz&rw}{v7W>!@Y-hLr_TP6u-|hMO_Y!e3{M~RP40yQz;(Jpdc5%$0 zm_2el5`WNmap1rX`qPYJHju05>5u$%dH0v<9d}+ZTaPcV0~+=};+9P1OA$6u8BA4s z!6(z_qR#@Tcp<fHN)S+*(nSCF=K@Cihx+e@xGr&L;~oU<HEohM2Gk-ohfjVu18-fD zY;0*rGT0z!aG)h{HbnO-l^fL^e9HUm@*WE+PDLyKA8FrKwNb4AACvcV@3v5CU(^Fp zlfkE~&sOipKs~3P=eZvV+=uF2^%8h5_NwHS2HJJ)f_C5sZz#F+-{NK2V161b@s=Z& zw@L~}rGGA?l=P>bA;zFX(DlG6ft8`uz^J0APr&OfTWuSl9%K-GfBk-N?Qm^%Z3mU1 zPN_ps+^V=y@#yb=ZtP{MW&}c8gmw+}hASUmnR3M+TtzNlS1M@DwMMEYa7^uD>(Bh6 zQbZB~DoMqw7*I}bsyslUP(U29#DF>6oMT=LVO>Jcgw%lGeL=m07J;d}>9T1j|MeFo zbwBsf3vBVWGq!0ERUzu@$l(w?ET|}G0T};bXl1zP`}pCFH{M_wY>qIW1gpU^&(aU9 z^DIwV%0blV$f$@>VC`XPX*ql6?fR*>U3}@{p^GzNVf#hTFG+_Z-HrwxZwHoL=CS5F zVC!dfTbF`rL<8#mAb!=UN@cKKv%F(z4VGlf8O!_Mx9?8J>o57Ker$Yh)7Z_0Fn#!k zp)(G{^aCGGn$Z+8BF;?9I0)BYxIXy$z3@@y%(rHhg6XY3w9i-xpMUU0(9%i3P<@0x z@;1hC$(5ysN`@dq5d;kmGzZRt$XyYGBEACKaqC;wTA=o*o$BkK$vewgsg{H9LZ7-m zS0JKR_>Qo8VC`nXvg7+W|EIWzV_h+!pk}FA+DSN*eCExwZGI+j;e-<g=JBSMrcw}G zCiu&sKnQywG%(}=u&g#uHn#*)NTI@yj7zs=mZcY1>sksdTOlGLVs&^a@Ojp|toMFU zX=<BV2i857@s@@V=@T(0q7(SO;#10JBWQJ1HC66s;xLJq)kN@p#wXHe4U{?%H6-d4 z@LBBL#ajopTz#%S`~C9bZO7wIk1HsL$^-8uUX{Fdf-&2;!#ErKKJdNAcRQ3i9`#7n zd*D6VE6ytu)FV91(BFRC&|Xz9suAG5%&WTBH(+S2Ro7zw&lkG`+E?Jc$*Zx~aWG8R z#%e?V<MYpg_foHFUf+OLL#v>L-a+2~wfXIN%Oe&mSZ`Y9S?-14Ho+M|!yqIr_#eS} z;N9N)px4kJ^y9Z4PX{xQ-e3!|?X^vUsBa>NMec^sf{=+J@nCY94x1Kp+qVp98?;s0 z9Plaa^QCtmi26LTS;P?V9^!S=YX+!z6|Ev}V?N*JIQ_%+`yC#4emDPX`O6z`yukFN zv7xaPguNeHKI8=m%L{!m^e7ky8(SHZ0h2Oex{LBx1QCRT<*fOi=FVW9WT|7x1@7S< z%6~r&zdPTs9=D9OR0pb3nTjQ!k0|+kAFw`escgvtl1L))4?I7+&HVLS^Uv>|zy9F) z?FsWld#b>u#+yrR=?W`8Sh;d_J#h7Mj&Ux%gFN@R8mfAO_g1fFUZ=oN!C*7o_>r$E zOSP%m^WeSNtEtz?@AKbpoj?DTudAJzcDBn8#>v%N=f&n5<}Xa6AYyd5HEa-s5d3Da z1B@RSo;KVE7%^hJ`{s++)3*C;SHO3muhr)b2!A51AZ!Bo_x1Dld;h=tamVBRH=ZZ_ zRrBRH=iTp)=l7{zsttIb^?K2(0z|Eg>=f|?_&((m?z0pOEwp-C;!poxQ-Nu}=|c#7 zG9V&g8H9BT4Gd`y>K)!zFW>(6|Nq+l!^W1zSn!+bTh@0QMD~oxj(8WW{VexbE_}cL z4a2knTI*jwUVh5uyvvPq(qZ9!i?%K)f^Rd9CLinZBkh`RoM;>X0bl!f^H&hIF0_5< zTrd_Jj~Q2iVS@IwHV^_Y1dIv@hp^z#PeVI^d7){zsR|JC>+<d|)jRIgt&}qEPbgtF zpqbtV3J=;t?OWiU@%J=OS`53M+`V$oGf@1PbDHx4sBUzl3smk|sa(YfPzRKsdIQ`6 z?l5;LAm{&X*3!ouYfgvu@3*tIc?A;AlzXY%aL{tKW!hpWnpE^+ac$UfFl~NXIHaB2 z*?rfipa<z;dIVJJTXB0z3utO@oY$luSl!k_%W80JFUl(#3|WV=dS>qdtW@U=u#pwa zgy@RV-$omt&-=ah^cVz-PA+Nud3Q*E=JcsE+rThNdsOQQ-A;FTuk%P?CI3_*Fn`v< zj*DIbI?{}p{~adtHS=oI)6hY+J>0efB;+JSBuoXZsa99L0R=e)zJ<@j+N$ezud4&c zYoDljvO4H#?BZuC!o^dIXE{Fuy^%geH$w69;?Iiy1-wfL`@k#Ew$;`R0y_n64!9Q% zb~@~Jcp~V>bd&B|vXP?Su>Z}NF+<Gi(nq4hq0Rc%2U^8I(DlIZzzM)3Jc=1E=Um>F z6A#NTtz58bBILZ66QA=hNIa6bB4H@Bn$_}hix%K>!ut{LCE!`7zo=J%llsYur^dqa z?3J5W<$%-RZ0j5Y9cs4!SKH<g8WdU(x(+n6c0^kazJ)&3e9pkq@a5Z9G=zfH1(yrL zeto+x-<NYO=b@jDn_r4wQ@<1#(*1#vgRa1ub88=4HxpuCjvW`<5n^hW?p`_?X7KTf znNz`z14k9G9JRb^=?4Sr-uFfSPavnx<rO(C;q#Z5bz8m+DjHI)$#38Oc>N_m)eq-M zr<b!SICna2&L*H%tMY0Lc*g6Ip4UJxtG}rSgJX8l{Gzf@oK)Pv`2d)6%?YNm&?&vc z;r5S}jN?7OJdRC8=B4II(4k!0er>8j!ea@w%cX<1SbJJ~8VXn2kJ__g*WBIVyVpY6 zu(ZLujsWw413)WU(;5<HBs^a34Ag(R{>FO8!Lr-j(Yy?D`{llx7X-_<u6$`_G@J`O zZ@q8~)OcRvS%{etGqLn$$kp;(xl<wU%{;%ncYyyanwc<Rf>PI`5+a8~+o0AJT33aT zh9OS`KLD)aX_X8&;&Q*bkpZjEuNk?vI-K=C_w4x@KNy$wLXL%`19cg$E<<2y;MIU- z;JE3y?C1=;7k`uTO<$<^Xo@wpJor!XZ|+wOTsf|@uA8u}-1f@bd%*VGwESHU0U<y+ zpdR(95Am+}x63{N_1~-KUAGV{y)AajYv8!-&>Tsy;oQdizY2we3lFtB+#K-3AL||D z#jUJi4wTBhy?A7JogIEI11O}B!rS=El26cQLA_vb@ck?9D}pl#86TW|17cc~_Kuze zW|!G%c0plKVL5wiSd*~!(AqcPSk)6%PF4a-1#_->>Fpmkg3ATJ8MG4ywjD6J|4LZj zcEigXXTp9Ca1f}_`ku#<CPDjkZL72igN;#NC2hJ6yNr8`-xwibTf)8N7DA^v9j|oA zfw?p1&0VkpvZF7*mA&-W=lv7)Pu4kA6FPKlKca2OPv^Jg<yP)o)flR#S6Nv(31V|% z>cuPvrm&00;h{Q1x;(fVK7VIv_Oh06w$ItvbDQtrc$%qa>o=kN+VcNO><BGaw(vCX z3qI-Id%PQh-k+D40GFCy+<hqqz9_rwv*i=v#xuDExsL;o|D%6DDl{B7Y=IW>%|2;b z3sO6$WG5d2<49wYF%g^}J1;nU!=6FkRNVU|Y^m|}(rtS|Kj1m;$@r1^JT)&kZyJ<p zc6;Nrz*#W5U<V9rJ7C=Jvft;-Tex5~D35xHhi?D4^P7E_zjFV#@|Sg<ZMgL;ba|tb zZ^yC__g1Vab`t0(^bGw;$f};bGTR4?c4GtMRbVe^xbHk3f0z8VQ{UBp_bO=DsqKK) z+aYjZU{v5hm|ZyM>SyDCf%+i*LFn7C_nMwBfZ1-YY<?SN`p@d~i3Qv}+%LI5g<i{g z{6F^YJ35Mbef;=)b~c-AdMBh((&)W~-a&frO*#TrP*4;V#RejvC`F`*^e(*xNN7Tk zno2?vNFlv$o0;Dq;5GLvDhYUh-s`;1@o<hwc4wb?p6BzK&rZ<{`~XZZm`|EU!nQ8k z-`MF2f!hPOSN{v<XHVR}4(zetI~6n<&1y{r`j60zCNz46{mZIi^WE~+x3Ee0Uicom zztt_UYXUfLbl&Fl25jiLanYtm;F<4P?9t}Q^SR)w0!!gX@cg8)t48;MOS3L5y*wGh z@0@-2?EAl-(bhDt$xDqtgjQiK=Z1X&>w`Bwzv&Cm=4-zeD^NeeyUgc}-}dXmxrLg- z52088o<BU-7TjXp*1PQk`H-|h@&@;P9#cHp0ujoqND${8-#%_Q?7grr{>KSmYGj^n z{<{3fiT|zja;eGXj4OfAWJ;qG4Zj6#L#>zA0eZ}M?p(KHP-kiF5w*U8&1<*$Y@Y&w zUsmr`ty1~(-Fl$TTNhpac>+g(_-A)~Sl)Vh+P*7J*7Vi1h7P9o-P@)@z1X^^>&*k* zkGdbN!ogx?X=RxOw?^I$js64<jXCUoWDYp*aPH$=1;)1=7eB@i?gyt0Pp=1+->%%& zOM>*{=>yU|VSCh$-Mhv?ccJS`UCuxG@xR***D0<ZFg|45`7sWV+#;n}YB>1R_IB{T z2D)v!AFQ@Q{{4da1>eEm`TK157l7R&yXp30q3@L5AN73x5AtQg*5qyV!SbqfTZ;K@ zT%DX9zjt(k$-O2H9-jn<9vnV%v<U9~c;E5<5Ex(k;dZ5-x!3$&Ja{*#eA#<DSp94j zrTqpHO72X#(+WyQmlhbNLyc{r$3sFv+#_xgk3xob#`Br;VEd(=b9T*!{Hg`76<Ae# zy`$o1m|1h0lYjksnKDS}qYQ$C;R(qJi^09r?G?8H5U?R2*1yNEXO`&JT948BKuA<@ zM$k~W`_kRhck6>C$5LP^E;CT<7xoEzq3zr@eyyz`sB_R;K{X+A_BrSCCfJ;~_3dpt zAbDC!M#^w#ZE2O+@;#_}zlvRz$>6-oIm~%2bdcIbv<rs(p#`D&4zN9X=j*#xK*Wa; zKSyo?-?~1ly}N>@ho*z3O_{*0(X0@bfVfgzE-nJiNzDn(p)w!1u25H~1EQzsD%yis zDi(^_Kqw&uJ!arr%_^2do1|8AT7^O2xWKsTAH&&85idl%0b84H_udf(1ww&aff>5` zb!pwX6WH#vecsmn&*Ps{IcH6_8Lk9Ijl6mu3_lo+rdq%LJYYpY6aP36H;Nm?Es&U> zxG|{(prD{UgHsvvG!Ha!5H_Rv{N|s6PO|z@mkk?dZtDKS7T788dVhBmShdnkwF-h- zjx{&bs0WT69q&2xgKiOB=66Yg0;RyVpbu<)cf0G3)^PTVvpXV&L7+|GXVneR$`JNl zi<uxyW(t!)%+%ZyQ^4&j*Qu^;A?a3<f6`&tR&9s<&gPI@KY3nCyYg?B(3{?L{r$KJ z0fLWE8N`+1GI0?ID>YwemO!*u%#@hr;8^4s=`aea?5N@$u(-^LiCcYM_Pz_Qqg<N1 z+<;gq?rvNsaPoHycWhYkeEl=eYX1v6g>Az6^5?N#cRPdKHfYzRO<Jqt5Zod7T#!GU z-+W=i#a6JT%eFc@zJRndY3I^kfR^pTZifYe`xf`k?(56sS$9oaO<QpBci!l-2@V`T z*!@rq*p;>W)jbPAcVCxcwE@E3ZvJ`mm7qDN3D@ieZID*0y$7D}dMxu?30VbMj#)Lp zY%w=7k9tf#*A{9Efl#k|U6;CkAWYVr5f;JjQG0jny9j%C?{B?-3FtnuYNKlhVar;$ zHh&kyo|+b#n!o0y>-VmE-TFavU`#=bKWsU$ZRd_iFitQXG3|l2y<0oCo(eJfu}-nG zVbj#jgSWg7IaPAT<T^pUPwM5>TL8MnRzs|+m)X9dn%<h;;N$82p7&%p6LR*2h(Oq0 zZ|CV<m7qXd;8I|Lj;A`Lw!Z*QUQXwnW>&l}3Lb)&;8KBjw9(YmRNy9^nzou&nl@n7 z*ecMf68O6NzUh+>C+tqv3vUiP>+e3bCmIar3~{BOL6_jpQ#*G6yY{vQ+t(^SZse~o zxL5ch6nYhHEcynj^z?7+{~2f|3*&_$5ZIww>uRrom6w&qsuZfeSmk_`^`N<=iO~EA zRZayg4_FHZ@6ryX6CmqiR!ml-3iSO|wmoe%f8O5`&m=k~-GPIeL%oj7gOqPmYo)#o z>0e}w$yf{<H*DItxgFd+d3RXygNnz+afxFK$6V+>xT{l_*--p(QMaPkV0-$GnY+G( zBXy5VKRO58TDm2=9)xa&E>k;y1EQUnC&vE4r2;OMPFAW84casqSpNiEjsE%IweMlI z|F<sR9f#=sF|WoZK**Yq=-`h)?`(6>X3+1~v!#ROb?K|}=V{@VqWI!r&u+hG-^(t} z?pg71=c&iz-a=2I-TzSky6XJ4QvkGkzfGOi76_Q)AL1Vl5j!J|=N7|OukE%w8iFz3 zSku%JoL4wibIN}P@%S(4UscWqWCaX`)B~y8?j?ZxM{b2~%farR?Ps>V!G4K-XZzDo zX{Tpp&tu@W)3u#zCvZLJ`kJc%)`zSoT2}>+njRb7YeU-Zv|DM_z&^%)zMb;d&Ci~+ zf8~6Vla$+`;{E4Y_b<&U;e@dFKTzN9c;F7=9Wh#S1H|59cd;XA-qg&|%qV|87p*DE zDC!0e9J2OieFfEKRQIYn2ee7rliE!nmWmI=n-J&}m{`3L<gCc4oZAp`&*!=1zE$!1 z`Q7<fZSz?3C%@)NrB^-iTn%|bUj6(RVau0W=WW{tmyZ0j`X>_vR}K0;r~tyMwJ<eb z4?>QRC8Yjy>gA_XKWQ&V!@-7!j~}@O7Jo}qOItW*I+1$v1RUsa=<H!Dup4Vv#kK<Z zu00^G)V^QwdBX4Z!vamVCgHEImx}gX+b#OB`Z#d?!Sy{C69lIP#Rs>CTib4ryLBGE zAM``VEr;QDZ1h*rVNh>#-Iwb)fOm-Z_{uS5-nY;y)9NlbzU|n;p$QyzJ~HCy%W(9w zWAVoY2wT&lZFBGPAOHXB_3f=>t2}V-==_q?IM`Qf|Gw!M;8p+tAOJ~3K~x_-VQ<X7 z0Y82T`Uf`e*$jhbA2z$$Gy;<LB#B8M|9QSh_a(FR2WNH(o*Fmd28gS~CE`NR)YM!Q z&Xw7&5~~8MdtmpL-J5ncuxs|7aeLash3=Ow{4@%Jj|CM6tpp#%=WB0&*m-{Up*>M> z@~!YCr+<J-tt*vQa)X*<YkVAf^6A$*Dt?ApDV-2NDM3a6uKZOiW)Ba%v%6$l=Zu7G zUC#a-M`&PO?~{5pAmQMh&54a5P+NUm^-bWk$9bspWjML|RL<#A2nY!9@c#ms!U1lA zHeB2-&Ii9hzjMCUkn(==gOq59@QQRi?*-DUa#v{%WOdB?GJ7FRY&?F-xRFrxYE}Cx zd%=E={YAUuV6#m>K;Hs(tk@N@`(?O0Ik|pHF$fKXp28sT>gSbTX$92mP~%t)fGb@2 zE6&IhXJ~tfEg&E=z|MatWQIPt{2&f8c4azcx`DEo`7D6jtzxFcOoooL+n;XtKKPh@ zZg~%Y*nV;R!7bs)J(8E3{}vqP=!RqcfGm6fgco^{84ys-{|mo#$Uju@pdbzM-^st6 zU;67A!B2f@ZZ)b=4Y)4c7Ggmcr5mJk1OK^xg5M5^b&s1I=K~wMZ*<x8E|_MS)|<j1 z^j64@;N4(<)qb_zY=~%e?yYlqptv!Qk07H}=Aj3>A$xRIT6SyjTi|!pZ!w6jqL(-Z zc=UjRoXNRA=QV@lb54vpwE_$;8rK^WK;y0XOmhKvQ)phE?IIKlRUjbGzp>v(zm1#S zM7!2@kBP(G+`F}s!ytD--mttaP-9L=YRG(u866uImjN{^*GLJi3I#I@Un;B*ah8PF z?~H+vi9v4%E(Dw1`k~J<U;k|X)4QAv<)ZkV_yGho33$){-jnCC-!;E8zIyPx<M+OQ zB}g`<Sf$#-nV%xQjBE;$Uh<IULRzKt<g`~HWOZ<x;5J}tY_4kR2Gk*vG*En$ASDQH zwTSk<-5hQ=jeaF25QGlG7U5f{+q_QqI!nOm9p@%auYq=^*i*Cyo9}E&Z4QFV0@v{_ zZgBI~tr@o-fV@{(q8$F$S+c)g3UOb#ujIh7s1xs<%mJHx>#f#D!J)vx-TpnWTVl7y zZZYJo%YQC!8aP-vv~chOu~hUEwZPp+b-6nCc5VxZTyXyN3#Y*tYMf?V2PyqimZhEr zt3s`<b_7HWiR4@!lzJF08umlNONm*DccIbZ2Adlcfhda~i4Sjn+=Bp67AuRCWf1Kb z^Ga-MxYXzJ(JKz1I4T2`TrhuY?ry#d!;C}Shx`n_M}0$mW`Q_Q6QL<7f1W9+N{X@^ zj%_JtJTk=Eq7~TRw13s!2=&+3+ferd5GDwtgu!Ko4==oxst}VEGbQdNkdb3$Kxs@1 zTEmS|H=5pvf~b2}uUs{OvV|=6gC<n?K=TRoo8PxiZy)fP>NCar8}QTkJ@A#vKW^L! zPs&Vs1=2&(lha#*$5fB$9;-lqOFv)#I#iM>b*m&pVoK5vNjmU2>S^>agFeG1-exX% zE%FMh6b%{GGInKZK$<Fdm$yMcTtJEc#B%igjXu}BH9#$P{84}7MeYC?zc%(ZE&<DB z%L&Up5Z)Bt61G7`VrHw%ULd5hfLV{?(!cz1v8`v<OYaBz7=5a(E$pz{_0{f^ker?} zDRl@4GkJl5;4#xf?=c8!2sNGy9Syn%R_Co20(K1H)?@mUwO}haf^dX`>;<<wZjNp< z!TFZ6)_D$`S`vQybPJHCN}Hr2Fr*mt#vj0;g+qdU$ZzXedRP8XS_a3)oY;Tz)hEwW zB~-2_7ee~pXS3f;7IRheT;RH}MzDGEamR6yQzOSKQ2+J1YwE81^ExSXFQjnwV=m=7 zr(963!?C0jKmITDuYke++5YvxXNS)KpU)wAQOdRy7dUGh8FS79B!4MO`T%m0b4KKj zfk{71d}Vw*umq@4@px!+wDq+>P1j)iploov;5ymW_P6mada>xEq6~=2yIS;f1Hgq? z{*(RdN>$q`yWz^FsPC`VgLbpp9d7FcRidig4)`9d_Gp)D6QKBqlC+W!L0W8yl-5F! zO;D}svmxCxJt)HktTyOg(rty<l(?%2KZ5^2zs-Kb|GN3vllHF~M?&ueCxQNsKGW8z z{QbxO1%EGJ=ZgRRZ~9k&;7<T>U07VMd1b7yO^ARxnYG&0e3E?H@pIy}$NXGQR_-d9 zaP0btBjw3wc9V$_V(!N{#+spR{WkBkb_16eoliK;152{ymL(UwUaItNB@>)?y5M|q z448(S=9v$|v8ojt|81K7&iw1Ja!5G^40-qr?xJst;)~tj(8?n(9$5*6ZHvssUqVWF z%DU7$;8)jghHn)3f9yZquK=u%>L%y}@LO8WaqYjgUf>R1ke`#kqhJjv9cjxYC|pyx zq3|cj8J@c$_hYEzT05tv8T5Dbt8J%0dA{pU>%X!21pH*b2fl&jpC_c-Wau+~xe}`E zEugAVm8y@94-fuz<6%YTjg?PSK2rHhNNkt*dg54EcXz|sjn-g2-g=64BXC^rxZmL| zxL<gGSz0{QU0SEG&WGSp+v81-)sWINg;YS<s_az`!xbqi>uMbQ>~qa@-2zsvwF9+h zA>e#KFMmB`hCXOip5y;K^2i62NF_?S4%b|+@4sFRV&dZ*;--T*R^y>*3-zt)kE*)> z97Ow#4*S6}RO%z8L4J1rj)Lgl&liRRE1qKcXRES9`4O%yyB2o68^rHU2)z>s={^}7 zGA==rO^t>(yaq7`Vj^QwA?{Rs-vnn!?VsB5o*9~@HM!gP5;&DOz3p@qxWo)Dm4ChC z=@-m0m`*7mvGQ4*N%Uu=zS48jV7RT1z7X95n#DCyn%n}{JuW?6=0d%+y2*8#LgwJi z_L*HF?X@&bS{$(L|12<#7VShUus>-h*mZzRUFNV%BS?ef>QW+*4djAhtI=%qhLSf* zmz2H_E<d;=I8Ou1^HQ-T7o?Gv4VJGVYkgMZ>^z{D7r6mSO{K2V2y(1)TIF5`rJ54L zkVgv;ga7TK=!eQ)Q*8#UAD&{0CzbGef(0`{b61Q6`((Qe`!%3%r?=Rggq~-g`@H*C zz(Bg<3v3lW6xM^eo%w6?H{j61VVT2sfP^;=6-WS`_P+KEcvka_@{EIQ$E;=9UxT@) zxw`o_h-PuSI1iNboF@^)kHsru640L^*gU$~pP|ePE)d>f254?*95l}Zr@2TPNO^Lx z+ylZJhW9(&1v>e43g}=BEgObSZP5r?4{Eipr9Wh>&8U{y1&-7{cIEgWa4T^8)b%ik z>qIAUI<)z|m0L?6P_`)Dly-oY<-7z|A6U(?nhg&OSx2*SK+{gsUZa0>3NT=5W*l!E z4bpQ`tn|x0!xBKwlS^bBgpUmGRG#h1yp&m-`NgBSUHfg^066!UQ$^R9(oLRlXVIMl ziA50nLhwgHjlkh$`#`&C;Mdf5v0pGGO-wp_w+R?m8&iz+!Nt}k-(@LCBQ2|+DqqXF z{{gsMj)mVIVxTC3eX`xj^5n7OWXFAuAAx;<owMy>$n2JxnfV+@Z%ehLE<h%kWI{<s ziGS&0i0XBfpZfyPj5A<|Ew+K63{{3GL!hu}p-W+NAdWb;f}&NliWbaXW}{gF%~9c~ z<^=FI8a{`Bf&RDrbfA4l>?*oLV!I@}yJG=sWEwu^K>nt1or_R^W!<E@KSGNm&0lOL zg7qrvvDOwaJuodWuL9j{-F&MTA-`4r?t*hbLqxs+?jmy>Oh23Im_kARN;)B}1!gdh z4IodFOQhLg@v%Ix2q5d^rn3K|dOHJBO-obDC%`_exe27;QucCn#ZL)QYC_KXoIyFe zL7A?I$`+uIB8uR_a}S1O{S5L*$z6&7S09%VF8e`at4YyRAkT!&!Vf|yG<Z<&cHPzG zh(~=&cpn_QI0ZQ#gj(iW{x$1>#zC`FlUU~SER_@FMUa!6t<PNt8D#FsyaISC0g69l zduD%^6A#KZ-sB|wa)aQE8JW?UlYr^iau(cwbjx(h0Q*cko!#K_j~fKyfe!9`m$)hE zB?yiSY8LbXc!gJ*UFjom%y$ZOG{W)zC%sM{fPOdo%;^0x_-cG-_<Reli(P$P;~=tK zWXZW9pgAe55*C8JZ0BwNW;y!4t^GKMeh;^m*}vc4p15wGc0Ao}>1z1_q&KAPmVJ;t zJ$qnI44~x<C7>+i6F!5S*}3jHYe3)6X1Pr+*xs=H#&!-c5_rs+oM}_jj;9TS$Pdq1 zoj(k<zpT}?mLm-MWI*kHO`znnl4GSwaO2F)$eSzRq*wTW(=r4M^xy8+<G1xJScEj8 z1~gb%zC2ZCDkaMMaOb<kCQ0L;e!p7=TE<x3g77}!olakS@^MGgUJTasfLg`1zI&27 zDcn(*Uii2#O0tA>A+h{%_`CKmx81JkuC>5!tnDn@ZjfP<aW>;HNVBEB(hEQ~*<?fM z%+htG(NN-1B9t_H^i^(2#p5AN(A3dvf@=2FtgF5Ueh$8ed>`|rb9UWq`|Nq}bEj)7 zu2%y-=ODBH^Zl#nY|$4*-$C}9*<a<1fPfNzoxdOGCh9I(xkLK-^w9JnV0JWznBN5d zm;BQG-U9t-o3S>_AkRK;d|n~s49eM*^DcxG1b-G-3&eGzyZGX>pPxPPrB*{-vfb){ z^oF#hBK^m0s7ton*^2-D|LtFLkQ|~kgz)a=n^&d}NgtSg5UQ=MHlS+WpXbxHxz}I1 zaU1xQga20F(ZVg^3N%<*FScCu9rSQKzV-EOm)jpfn0*V!<}Og<QjNhio&)n}lZ$C6 zD8Y&+%ixC1&E2<ZfN)*7DwrzJPaci`0R#PS`89gX{7XhId#|SAy(LwOtAo+XxZ1P_ zfSmvqKdF^80g6W!Eh@eTZb5FZy4i!(Z7VmcSHXU+-3q(bYP~#dz1SbKf5ol?*b8<Y z?QF{*Pr+4qSJ?GvBg#CyucF6c<sA<zI&XA)*Y%pqPRLrAJvr+l^g7#P>~rV9C(}FE z`zlB+q`FctnAclgwrm2;2bvZd1%ysQC7}}h^2*8^%A3lYP*hMftN2SGg;}J6*hB0u z)-3;V(=W&Wd8;2Sm<?0LDC42PyKqiX7;vBaJO@glVo;pH;%Uye)CTJrx-9EF5F)t8 zbHFFS!oTne!v)G`$|q1<R8m;#4rCL~D<Ip;I#~j_kFs1D3&!5YKBhN;3^I5CilEd` zyg^zaFO#o;a7tJv>;>%bpfdk)S}O4kT0n#GKe1p&Hd$oDwU@6|ztISqG;CDdC<<D9 z+w9e*qrkVG&w8J92)}dY*qM4@sVX(LJiJN%63RUOTRJ8wk_!}lUtGP!4IE!^nC{pB z#P2n>;(3s|$z!BBU^Cd-%KARoHrCJ3uY=rSxo&v@kUJ+w%q;?OleVvRBDgnqJL~2T z#T!fPN_v10E*ujMgKM_yaThNT5`>QYa#Q~gK+2bLq{e^;E`mGgChG*<%fH-M4K&%p zb>TGV8tMk>4uJ_1=0Cc5XNWvdo({$Bi+2_GhpbPt4rVok-5>Amxo<qkHRM`yGthjj zsjEo<Yai>Qy82L<QWR4>9Pq}Q7s|go^k?+%+Oz}L8ZN6{szG#d^j9(8fc&~5DxZM- zwp>%L4W^C8d&V>1p}2c`ybsz5;&!neSe8pWrHg<A_Sl1Cp+mT1M{qc6zuCSs#4jx! zQqmR#SKh@Qa^3P~<=ud7uk6U%xeKflbuFx?Lg@D)A;Ht2)uLA8TiQaKgRO;@pTn(= zx2>WFgJqE=QMwPi!gk-=tB3UWGF&p+0G@&;9$@{FE=rdS(s=1Zc^#NfoAb?|K3d8I z$S0pXz=8!!nTbUh&1np|9(l9!wm$iG9hh@)>ETRp-Q=>`rR#6w2CON2M$*3;#|JFr zbGWwbdhP3PLH#@RLh4?G`c3M8P~Qt2QykqLroy$<>u=nM2Bm@0P-z3XKju2+`hmDj zJE$W0TG9PqHdarxAf7CFNR6TRZn1Z9%O}s{Qk~&}VJFm{SnHLV--1&&r%=cJpbZp* z#c;4hN=ShI9h>KDk|6MIb*jyUyD7=nQ+j~(vxM{xAo1`QPjiN|KxH~TybI)`{hmn% z89=p%-&it47$OXYfSmz3{x3p7aG_)2dB`O%Hg`J!o%!%Fw_G@GwMh3oH0|0ntVsam zbjZ!m4TgQ~_b)zh3ry!t+f37-uT}4_dYu3V9B_EtW190QrU<Y++V0<8G;jXbIlARJ zqccT_5pIF&P}lEWr+~0WI3Vaj8S!vHb>HZ|#=Sm>&Z58A9&%gfewwF%##vJ!yi)!= zvl?pE$Z7&Kjcpp%<frn-<6dCu%zJHNP4wDt*Np<9Db;9Drf%Zti6@xz%^~Iq;NHLu zw<|z~a92nMxBhO!T?0Uv#z%|+Wh>j+51uZbex8efJWB9^tW(*QvqwWw)nexoyYi14 zib+|k#6e<O;?SfbkSa+bQgdkR)_8uS8<0OXZ&|(;?)%>_NV^BwtFo(SdqVx>`WNcW z1*^}rUucaGe>0(Pf(+6fX|t4B{(31IU;I{y4^;oG+Qq7W(BFO?V33f1y$6kOS4bC< zfhmO10;I`LH=btyl(Rf19>kBuA=-i9rgdBFdI*ZMOKeLrL3okrOas?%UG%P_pj0r7 zHjIXnppx??4<~9T9(#t_MIl_+4j06WvoCdl3-4X*e(8Jgxax7pBM-u6ws^hSThR1y z(-Td;fSAbGn{f^h&^(}v|H<Fhv-LpTDBW;q`fk&(CUW`nG|@Y8ZBp~6-|s?GCZT^c z?reJUac5im?H}xvV9k$f=db(f&+9~QvU&S)53jWzsPoh%R6GuUxBiuVGiO@Pmrxv9 z@@dHta9rYe+3^C1KZ@bv3XobzeWhuj|5V>Y?*_JwZTH!J1zbR2Y{lat$4UuO9vs+o zAmPvr5Vwkp#K$}xQ4W;b$S1%&)AG6HCh+;AAN+^=mwC6@Y}P{BxU@z0Pk`^Iz5%{k z&`COb-BCFI%K4KQ7J{LcA<pm>bne%=LnkNDEwmc1>joLeGbUs#2h&P(iCKc2xjA?9 znuBGJ#Z@YJ_VcqR?O)khxqWk-K<psyu1NpMzLDEEXLZH@{{Qwbp)rl954kqwo9}Lo z*w$jlIXLjafqjQMJV8FqvwUlLcu8nwnwFWL+_c(Y_1dHPNd?B^qS-~aN(Mv9rqs~X zH=+7Z)w)!T0?AQ2DXoQjy8F%VU4ep)1>FmBz<QO=Q@6MR{p8Q*Uo##qi9KZZoNba_ z3|KF<o}lXkCd@kXm!SPgJSwgMyQ%iC*hN8cNy+iz+hFcxo@qV<MHh>!m-PJy)k`k9 z<bvRgGXbD+)4Zjb3v9;+C&=!c+c#%S`SZQVwP;<j35q%uUoDwff%%iYQb&=2uh~e> z(;tVGcRZ}<yfG&_r&jK3P`9v-RQq%AU*&hz?-khRyMOS32)J`DanPLv@Cf$k<Iw{~ z=!S_yBOYzUga31-oeZRrrlbMRz=Db&H~n(_?}D?BGk}DQ1RPf)l}Mlli&;=+d@iKm zvG>~ilYGG%YjhxX*L2X-0%d?Qh|wUAR>sS(Joy!d+#^TH0URHG?tdTj0S$&nCn4$h z8`4lrUD^XOHvh>tE;JR6EZhKzhm+n&`WEWmscWn=8jK&CcA8?~M!g%?Z>0b^<diL} zUs&o$eWYoS5|QGSG6MYG_jB{R0WGV9t!h3DsvfJ-D4;8J4ewH~b0BDwv}xK@NUW16 zC-sKpw^N#?)&s+;(sPDV=#<%UMf(vDbRe)%^=8myN24PR$3e)vU@`a@2qT4&!iX~0 z$o`mfELQ{OZ_P2Ln^0$T?N@5e1MljUzpI=JO(!?))wmEUC3^Pre1fNsTIO56vh0Br zx74vIAA|2QUkBeTXt=Dw-umI-=H%w!b{5)3wK>sxCyZV@QW>!bJZgDlySJ|Re()RS z7wgv-w7s-$T31N4Ns35%@z+Lds%)BM+6;-Ul1lClf`Isd+5R1&lcwV*?dyTxXy5xj z4WY)&kYyojp!dCA@AMc6(nxuQyb5CG#Pp24584;C{j}9#;K>19`|XAy;|9Juur@f( zbnNe-flN)N_+U1eoy;0@EwG%m?6#bN<ly9WDQ%$i{nA|}2cd0r+bONrg3m`juY2Ex zsvD~`s1gZ7ErZ`3<OiL4b*S9_0tibrpKIQJf)(SJwF9(4<=-y9*L)4WbACT=f9Ap9 ztWDXSAY*RkuFT5NY(o?K#_b?W$a*t-9i)lrlheyI+T{0BTBbIsc)nJ2|Cg7giquXT zU;aFHUFb62<tii}Pu3;B2!2t%!+lGkRcy;qVP>d0r;0h?Iq2E5$HnK4L$~0r*Sb6a zy_f!Hn~hKLjlt{U7_kd@cK0-Srb1S~tl8OHz<kVHZ25zy!r+TNl|fiS4?Y0xEU|^y z1{}sYjIj5FIuB~^uVn*1c0PN&NB!p;yYPzesxSjW*M#&7UILA+8y;-11A_E{9|o>~ zj!_+^w0j3ih0=zlBO%2$bzy4oQ{<U9c6e1h9&)%6soVf#hB4ImIvDmDi;P`<{nvQg z=xLk;@_jj1)|C02mpRW_sQq5;xY{$J{^0u4>&=9atHHg49H7m*R_2zApvX{EwWu%L z`{<tTe#?rF8^7G#yL4-5PH7{^+LHAkyD<d%RQIkn57Gvuo6>Wjw1Huf;TGKMe6Qa9 zqY(I9wFlKkLyleU;M~nnu)Od_k$c7K#Us)Ed-rnOWL&AF_z(gXttHQ52QFVYE6z6| zI5cQ`;5E>^D}E$?_!Q%5^OW$Es!-ajbbQHD=;YP$RENtDR2=A0-5(mfQa`AEb*QzZ zW@e3TkTx#OlqP}6**w7f2Tw1mF}KEz&?7ME`h+XbyF&v*gM0OxL-Cp7Q$;r*|4#n3 z{PrLl<N~<_<V-nQmj0lg{jtZV;(7Y4_IpLg9l5F8O6gbrI{A&KjKklpe<jyU-kmZE z?hU$k{{AHJKj_!N?=G~=Zn3L*SE#0|`bE{1(0y;$UR@l(dbDn^uFo@w$D=QR&7&;u zS`PenfBf>V>2vb|b1ra;TSWi=>tD(@tYj6$7sMaD^8&cpxvg>Q47T(1?e%sLADGZP zVJjrQo|JPp5i|)xFQEr`IeQJS<P3KVcXlTA1*Nj$qxeIzQ_A9$+JD{r>`D7qT95S0 z>A@B6Kgq3AB2z-1UH{UG+8pi0^4GUAP#Mgaa^=(QvibC^<|lGLWr#c(ZVbC=zEuE~ zWX~y{5>$5b8spU(ZWZ2+jSd58gS=BdRq^^M@BGVtntiC95xP$8;@_nSRNYpkX+TG4 zqiKDv)jp^k;}z)T1}XPbnx;Gt#?MTrOsW68dNKAebvJ$rtmbo;LeSyBeS!7B!`|b# zdnlx=N(oF|Q2u-`y;)k@F#aj#PX<aUeRjuT6&(-D+jsA;OY=_a0m2V-6V?JhvPswu z`gZy(n+WhIa$n?82U<1{%WP2<G*%ikroaBU>1mJupEO^rzSX{=?F_w+_Z-t>7j#lO zMz)^-VmD1|O-=qWw2bCM&Vyi2XW9S}&-hflFO`+b8jwDgK9J%-E|MMPQjj+)Ta^QF z<$Bb@pO-*Lx8MQ6mm$7kyhFlPDCt!iR(b{WpV=(2sRq(<>8un4vX|^D*8-)J(n;wJ z;UiBUJCh3zCmdvlz7STcMc?L2L38WT(>f&6c9RdB(VX2JAwr^G(v_rdK$)mSDo${+ z_|)eolc0TK`{MS4VbIqD+Vu|xqm}V<qZX1aDG{kT;J4WKqR%_PLOxc)pmcj_sKEl4 zbFX|KH5eKnZWP<l6~;!7jvW~fg=dTE7gd4mE;+YzM39Hb?@42U-U#djd8PD?{60uo zlCyjflmJCm&cLaer++--3Xa1aMmsi#mN#1N49kGlomv&PG=j0G@ne%8T&Wl364e_r zYNZEdTm#N>h4gYvNPE(g9uTnHzmI<o6#iV)vFH`Z(dTr@eYikrq!q{$mFdb`aP+fd zBaROQt4OO;+9GIBqkg0Mi=g4A246S)286~!0~$feveJUm7&sywDLGadlHR{NELjF# zP_8Q?T(5rJ@5WEi*13&W>jyA$#qd$X9)J?9>{4z(R<o?B*&`t`J#yUn0LXL5yO*~d z4i7$(b<`a?Z|k(GqdN?C7`SP`Wl&lu^Of<C(IE4k%wmXKePPRm_8=F?&&%#0O_66v z!$HcE)<|yv=Q+xWGL34#<+`Oj+vOd({Ct0?H>=)(x&xr_=c1P%7dN4fP?vhZSx*1! zvfZ)`vYMs6Wdy`Fj!lef3*I4>>sD?Far5IZ#E$^8gSnBp9sn`KLf*u@$h@b>S1xc# zXamw&DOw5w*-dUQe+B`4{QLXg`2GF=8wE-Mg&?n$j>#)Q+9TbSRsvg<9h?AVhO$Iy z3#U4iFOSo^rGK4q4IccMm6Y8R9PT?@a}0szN1JtN8U<mN7W(GX!0?m7-tZlqJ9+-l zg*ot`@q=%&c7lF^{$u@=GM_I;_LV0Aqk;Qn&VV~ApO(&pv{V``tpjU+YrXXyaJ6x{ z?V19y@5bJaTLAJvd61kAm@#7p`88=?*?KkknChF3!sS|5;-bESuv;xm%@bf`{P5CY zg;1PP>{Z+ovioP(&Uq6ww?t1(H6W5@tODsnIYOHG`*|rXk(bIVL3&OaBuxUm*ux%R z+M@|=s+=xofpkual2(ASlod(>2;<2ST7$W_dAxZhr07%gQaeGL+}5G3Qo;J5^+oIN zz);`tlCe1)-+m(RqyYJI^E31J0TblqkGTK<AOJ~3K~#yvru=zk4l<86Rfo%iue?#N zcto+9J@D9Edy?^Gvhu1j7w(6r6{Zb=j;`(Rw_gdmGrH4O!B8@-bU^7oIQ07AK}Tjm zv3*HyNic+moIZBuMES=J01km=vvfwf3n^n#-${u8Z&&a4Dqn&4s|iaI#)HyDsiU|< z?8*4b@#mr8sD?i@m<snV-s^uq3oKFQQ|4GuzT_*uEq}da^vYPB5eU*&d8st;_w(pq z2SB5haq>*Kz2<g{=m7BgqS7$0<<NU+Pjinj*jat|w7p+Ic+b;Y&Wx<Ucv?%Drxe1$ zQ->puWJ6c6v(%{s2F)Dc-aj7*XE+_<ev>qZv@LMR<?zg-AAnRWosmX^JXd<TY<=;A za#%S7@s;AG1Rd0j2(=CU5?Y70Drk8P+G<<7w7L$aIi};LR&dF1$@tSMC>m1Kwy+_% ze&ur8b?|TN+4#IE*JKBm-?<V}kvz3*mM%#0)9-g=u2X^WP!3mS%JTsE(SLtt0UxvI zw|&vL)|6{1sX!e5ru{2#ZeC{I1vs_*^vkCU!F7&Hw99a4mDKW`uyLR$N)u%*q%X`E zoUt9UFXUwBoCbNc^uF{77-LO$OiL=B7yoVjD`jMAT<Ux%X<a<4*c$Ry<R8d)g`(2p z3ne{4iIjbn1V}%V-aI1+yiR+~^|FS<YDu*czXPBVXCOa5zfS&#aJcc2jH8~<et!0( z{cBf~-HY~q3I}%`o_u6{`TI{nv;27lPS37?nIkPZmMU=l#C5+LSN_8K);)Z>D;{{_ z2`HqI2BrY>IP)iPxz?4@QE3&*X9WckcP92qx(4|@3nmnN4B8lRgg6Fbr^H>4ivzN$ z#Ii^Iq-y!=$GW}sTI)h^b#uAxn*G~;Eg<~c^f@ag`>pI~aINW*?reaeDMQ{KTn7-* zpoO@$@pAkwi1_B5`vrec1f{t$zT(G;|E=|sJvQsJY*)CmIPrASJ5Xb0==P9FV87n} z8~Ybv^YdFLY;%EwwGMYbVtDd=w{(<7Ti$~3fv3ICq?JFO`UU!3`l-Jik1XfPc^p>J zdZ}RFEwL~Cyz~rQ^Sg26#v2egp?YccUC`-j$5|c5fxKVdAfJJ|Bk%T1o(<;L%=YFN zKt3-Q$O}M@QclQGAf1%%NcTV)D?2E4fD6DZP+BUjDstTP%kjU<9oM-o3x5CgNhXC9 zkUo~bkfwliS-vW50_7+zx%4Psy8V8>?Ao+@-X1M@`+EOWxfv8KE9z6c6g0;)o|>wl z7!`{W22z}qEIk0ZncQ0G3i3C~wLkjYEK<H;IY@^sl`ZE%&X5<#TOZzL1VGu$Hg<p% zA=ydYK;9<pl?DSdd66@}JudrY-biO&V9#IhMc(s~6E;j*q$mk(65<lvL5h@b%dyY! z4W=@Iv7jHV&(ePjrgT%7=|!;glV(fHKsY4q5w?TvHT?*^8H_7T*`|hINwip7LO__! zOS}NKr);0oXMxelIL)*dEJvkysg!@+m0mkoY${d)$CHjT9BY8>Lfg;v9+2;!U#nm` z6iqBXUOW(#J4%XDW?@OVB%BkDf&F2-ady$*IKr`~V{0h5RuEN?0)~SI*)SP&j=Dp- zIZ$F<GOr{7l=u0B&p~rRb5gSd?AqBz+kOBJ5e^p}20-4nyvupRL5`PW<-5RLlDG?o z3}Z9nTcF!uHO#6Ch)2c!;+J5EFnAbV0Od4iI0xEU;y|%3IAuAlavBLXuiMPC(Lr9H z{E>Ovq13Tta%nEe=akFJ&lOlHHEQFvhhfx%5kbS|gJDkTe8a1-Wy$u5I|4y=lfC8o zkGa>4Ml2MsfcrkT0dD8O?IX7pt~Ox)$o#JP5~P2S{zbY8idq#dFAe}@F7tS&OuUCM zkp5u3(q@>o8#vW>vU1u2mJoAQ^JpkE6m}?j9ZWaPm>U8Qc)$Y?rZbgEps%lw)qeuc z-?)6}+yYD+jH^uJAirwC%z|oQ_BD4jPyO{w5bGCo0lH)mW(gC8p<w80TxZMxu5guW z<=-y(vJ$Ig!l>ILT!&5nZQR(XY*e;Fpj_Rv`al?Tf8gl;Ua;rQeYf@taKrg#?OSE8 zy%xXGe5!d3Y{uGT+pGpduyLXBCP;TBL3+$>3M-ngztoAXo9$}bmSA~VIxD5Z@T8%k zgEu|-{>K_?tiigA&ZIjInmU@w8e1@&GXxrDf^w46oCUOK(N-XjA3ZHsY$$q&7I3=e zxY)5H*o?CoW?dEXit}>w+d%08L$;wAC{>hTr3MJ|nZs+pem>(aqpxuoSWZY$Qki?~ z=}N4&TFn6Qb#a0?5UR#knG|pY`o#6=)Vmt2zp&xR_xW(o?f&BXWllBLowj<zs`YQ{ zl}o}oArkEJZCBcM1jh)+1CECw|9pN-el}QQEoqh<(3mtOnm90I8o~@qKvzeXtlRwi zd8zAWRmmz5#Kqzmu^Sj>8?GCwfU=*190qNPHcGo2bfVQBtC>(bt2Di|GvJB~F2BaX zbj%cJ8VoiYZARD_!7<A*$?-ZAJYVp9{#+=URUB6`9+c;lK1%;*kY`JTrNYv3#3RvA z&+x&cz8CrA{mZk5N36#}n6UTx17izd|EdEU4u(V0%Sn^&{sazd9M(H5hrFrz@8sQv zl1asfOB7H(Wf4oBe%$z1A6+NT6UTyeZ|i*PO;Eb2)UR|F$S)|fln+3#5d^^mw%PiZ z_0C|LV>)TlfqAJV%98hQUtN{2Ual$8O8k@ew}0)|_=lIe>D_Hk*z^Pa>-w?!mmsfC z-rjr(q+arH>BXlQPbV;rVW79uAJD%J&fhwJ;#?mrS1cDTiy^mF-h#Z^V78b8&BK1Z zx76>|mvV}5B0#Jpn#8+cKh>_P-8HcDv2(Y(1NqzY59F6Zsj+l$=?)0F7vd3I9r{Xr zCiU+4`}Hh12sVOP@jQK2`@N#$;S<ll{-iIKI}U&M{$+<9wqUhTJ5C!4E^A!}yO@A{ za@hrWwR6Yi*?>4!Y$f`ER3PakKQN6oZ!xDleO~;x^e>?Y-RJ@~pWD>8NddW<(oAj- zMnB^;(@vm}LW;opC0(E{4aCY~ju-=_7fV}~ehSJueqc+P*ZYg-XHVL{<el;%DWd%S z=dbEtc;JS|Ur^t^_~HXLC#>JMZViJ=2Gt!94pzz9TUI~8`a>I6ecuc$8>EX;+Ee6n zLmE&IYz|sawXOofTw$6p6%28P=7#q`InCL!d0*c8shUw`Qa~j1iGTe3OQ<P?3ZXD~ z*npS&$AZI22U~|E*mP-2(3Tb8*xMn{VHHU2rH=Ap$PLYXE$;|e+DYRq^U7Q)@lUH4 zM;vhkt*E`LJq`A0c3;{x01Hwr%QPrzSoCJ`aS+02MuQ5}O9kWkr{mFIJRUxE-*v_r zXV5jYvaz}aPA8n^I`sy^Xr{D;+*^57@<)R8e%(l&D;S0v&l#;jz9;9%&R}~*KS*B$ zhRcQqhIc`lAkUXqJjFcl(Q%U?2qvNE4_^OKK3Ko2YpOGX&`M}7)B?kJ<6ff)NFs^k z-_MugNk!X>vq68zcA~yI$R2XA(gF-QhDL^oAnX!03+utwM*q3K4j5dGuNlvP93VH9 zyZ`=n39mg|-_akk9iT4))BDDACdV@CLj&kbFR(eOA7kSR7Ne!I)ELag=H})Ze=u)6 z!AZ{i6$@r}CN!HgP*D;>6WS(p0_m)rD5s$MA7?kvw#0TjOs+F=%=jB{>D9}FFE4~M zZf8eEDu59qCN)mwja$&QK>zB^p?AAp=X$OH`Auo6GzHduzhVByR*-Wpw^rUlb^rf= zSM}jjdAjcHT&dGf5Ogc>rNFJQI{(`pYo~x=l5v|+P{h4uP+ZLxHVO>x?izwaaA(i} z2?PlqoDkgIbpj+Lfe03K2<{ME2X}X8aA$BC?3;7Wn{(c`>ic(Z-CMP*rfSdb-rcKv zcdvfdTF*MWhxKQT289KfB$aR6=vr!<uM7hu3DQ9{uSKbsaCO(uVzZ_zRSwMxLog5J z-EwC!;JmvOlUJ6Qp4i2vK-$uS=HFudB>4K`4ci9FnvSHBT_=!QGaNa2uS{xO`-u=w zNJi^}LsTPvltsq^&BaO?+L`=7Vgj?hbpB*Z5q#fnw$~vQDF+kCVlVr^!3|UC$)Ob^ z3AzmKRI6(_hsUI}<l|UrP}MGeU$pa!V)|wAeBp(|b;~9}snPO#<HkiQKcc;d$40{B z(f6!`AyTWL_YJO#5AR}sG`3Yu#5vfPZQZPxgn6vyw==m8-dux8CRWp(>>C4aC+qIC z3oeSaZWH%1<ehV+@^C>(a{T!GIOZ<B%Rg?vuU>y65!ML46g6e%a)CJi64It_-SqiI zuILqcb~qeh(hK0w?f+IhDWzEuq<_J1U^Ft4R&Jx^qWCd{ZPi`g=W9rU%Rqs>xejN% zN{&{+pP$E9)Sh?m@h~qR{ZsS){s-c*i`bmDS0?zOTAzp3>8}d^SLokW-2brMQAJIY zEAI$3#ryEjqI(%?pvbrFNN6-(ZLj|JwtrQsk+;f@ABb+tuFKn}^WS#-r^*RCq@*%* z`G1zkK^Zk4vQsY%)BNYt1<=)qq<=d^%W49C{j+s&n~AS&o+`*#G^E0e|E~MLWeY<@ zc;c_odBAP?&yxOo_dm}(><W=;Ctikr{z8ra4>560$)rV&1@`(89`H-~Tj&0>yZ@3D zQ;NPVCi`su_Wz|$--=X^P;@}fBSQ^ioMs`iSH40TMDe+ht?Xj{&GR#A+xx2Tm#okj z8fW|V{68b}ABmJrlz*EMLslX3S9xgnIqf7ND0j*d-5rsi7@;n6nnZ?k!>WsW(00yD z%GT&hGW{xZ->6ArG!oU&*UTWhG!@+8)^=bi)5_g`xlRxxsw$X&aX4&NtKp&Ip;L(` zK_gLeI7^R?f$!S_p(<`FIWnNrCsF!``1eB6&;b&L$k^C$2$Zh4LJ5QZ8GXPKo%y>q z%!SoNu=a}{FS_xzThC2UJS4ndm-A7_kIHkj`pQ9+TIrD1&&`+Qc;1j>x{?*F{K#FS zK~nZ{%s9L>s<CNOoYcSgK)S_<?}L8l3%8J;W>Q@oEW~kE+`87%@Q|PX7g>PU*&OMY z&dq!;UBHK&k<tD;cm6%|be>T1L-<csbXq&Fs;&z9W2Z5nZ~H6ntAP*?vk)TuXHr53 zy))2*4M*<>A_<0_YsDT>xd79~H@8ffnCO{E2j273mCZ==xQF70{;qf3nUM-luRDE5 zjvufOwr2GznlajObNM5p5(B4EQsnoCrkPty8jZg1<3NL#oqii3&t6nDT1%q6C-9A` zC#3gm*qj?Hhe02#F{DvGmHFlS5ive_5?(XVF|sMK+%<0nyv9H2yQ0UuWV)m{Tt?4~ zhH{DBQ%5~EF6m8FnN%S|>lQPU6;O5P58_v=aUGQmXUf&l>@`7O1;UEuksiQB*$BFL z<n((f;-!>Oqd0_7_|Tl}ZM>ZqvTGNazHo)^Kju59%BP<Vv`9Hg`&CG#(hH)l!3*^@ zXVk}6SNm(Vbk6sI0gp~VW4D$D__i}6Z$eS;5dFP7&h<^DL)eTrNF!3Cr@v1#&&jEx z2In!h`8Fn|aFJT(0cc~$3jBeH8YRMPM$3Lb8bG8empo2ZV2Z;&t4V|w;ebe(h{M<% zMV<ZL6!2ah6AP7i{Kw0br-B~AAv?GIx2E-Fcp;{98Px`@@3#CdE3bz8`YYAjM906o z_|ef?M|F%bD8cbp1gdbq7v%-IS>hwe5xk0|0ElSWh%tPYgzxq_CKk#UY1`hzVGg3D zW-pdeG@b$<-eklwN^uoi8Q}7ztT!Q98cN;H3NBb6jE!6TX7WoK`{xB0oW2Cd!4o#* z+`!8voB;+jFYsyVIEnwWoz-zCRvnGfOZDm#FnNB;l$WMR*1_TFmh02iox-dzSD_Pw zmGu<_P?OWB81_v#l7VgyS=1<7q>&7R$qNJwCuyzgu4^x+#vU)8C|+sZvg0o`D*h_5 zr<b*GL|6knf4eEWQ&Y4*_sqh)58)&J6aSU@6Q$n4eyKQJvbW>4rOTJ}@9>BEhp3tH z5XnORA~`#8*xl`5k_cOiBocJpiA`f1rM~uRH6QicvEJi$U0+S%(&_<K?v#G{-6&=@ zoog@1WOea{OY{d=PUqfcf8;$_RgMHjo9RB{{D+Sr$fDZf8xu0;t!^-DHqvF?;S$XZ ztx93sFvlBICo?CL`90>HySI5)lGRa@)@HB=e>TSuub!+5?aXa`Aiie+39b{K0$zvt zLjU1U$sXCu@`Lrc_{^|?W*jelP;mLthi8;=tBX(R=U+KBS4|CQHfvvCO*wQ}z&b2r zj6p^qXNX?_olrVi{Zn_sDr&N{x!-v2vaRZ@Vj|l`6vq@3+xSNK2FfX#Bz+${9K<@W zopvj|=qb^u)A<7NO`J?7)w95JqC@G<4aDPF<2H`;$ge*R-6v}RED1nMwje8##IiRP z)W$LbGlEeHjXU0!^TD`Y-qS6*Mab_iqEk|*(5i+Ut+yc@Uumq3W&3p1IcY-R5QL;p zK9Ooi;|YD5e!hP;@fh)M*<WkqD(57z%XXXvFLn%i4vMvjk@qr1R-4nkJ#?bOYPAqq zzV(>)bWq+$VvGTHhWAa0EzP2amL4FNVDZ?ZZD&ZYoZs1r1>L^7`{_Qa_icz=(}5FK zPzkND3);KD4g&+_D&nc-`kP1<`pL>Oe#0tO&^LuQy{z!wJ+)D?k;aaRI_#l)Da(3; zx)PsHaXzxQAiL8(njF6pHR<DGonsoQAQuIUMGsqNv7zW8?LGQ>?>BsV{OZTI5xfxW z!qkGbhf(VnwjslwE%dfg)f4mLFOJ1)Hy1lxHw}L&ulbJRY2QzYjE;^T@4_eA6t-Ii zEphTIy~_5~QFP-H;{y58MJ@&5^0SkgES_}67pk;rl!%lPJZEnnMB+3!&9vw-=GCo9 z0CD%XaQ1?JWGfZ}R{!cVN({$$-%h^;S|WJ&t|QP93>;MMGCZ?J*Ec3KHvgH@cNi0u zI0VutIx2p*u^HfeJ2yi&<k6392nWA!5Bv8%9TyRO-K5N#>f)P^%uj&+n7%SltyVE` zYHdB1B26>~sYdgD5VBz%Il4Hq_{xSUzzMIcf};0fb%2UQfY(ME=W|ay2zpz?(q;q9 zzRnX;zroudjo2G5uxSk{GvcWg=oPZYALIro%h>2D3w#A^ggJS=QGCM}*JKEH+iiqa zwzKsGaPuV&4Px*Z;EcgR!ga1HuSvfuQ7_4HO*$rv&H^~G54_rh+LE05F%GLdKA&NI zsO<7uWWb<Pkc_Mql^f{I=jWkTC<-21slf$(E)%35KESQ;g2b*Y5jgKpoZB&n3iLlH z2R~Bq=s4uyg)VlXDdSK8MCfl6GE_JbbdWG7{v2-fq0^2L67i1z@G9QX?I0(4KeE1X zNS~H$CUs<(^NyPe`_MsT@&tRe>guPejgj13`sv{5hm2*H&!XIXxM7APGwdZNcyTk& z7<8mDoV%8!y&IY81_dJOklwM2#-%`S>tKwkV(gYTp_$+kYaS!URyIZ<bCItaSWI67 z#B0@;2SR!A$&qv7G_m{fHe=jLd}G)Rkpe=qLrkKw+Q%m6h@#0U^m)Xw)7PcC14K}k zd<u(>*u@$oq9k%d2Y#@EIPi>9lGu_U5lvx!EjO#O0o_^z1|8^dl*a0gc?&SPM-u+5 z8x#q<Ld$)oGj{a7($`v^5Uq#p@~tkfn|;wxj=9-(A$0p-{Vb(UgAeI+z~4DsRm;mN zb~hRZQvqt#ULT=S`%N3%H==0pGF7`*hIA!d=7}!Y2o^_3_-@F9)PJZV*I=?;oxzvO z5G<AX#6hllr0qnrogfRc_jow>zfNs;Dr#@fmYy(WGhS~(#W1O8j1BrwfSe5)d(+hI zQSU?OFL8}`0xPz1K$?>|u1>E9GP-ru-Qqx+S7{9&z1ZYC*l1#0_=xONKkn!*0SrTE zOvc<rNavypNiV{xTqZyh0iopi5t@Jl|A3Yyyg=!b=_X#;=R<oAMJ%BwFSS)IuOX!! z54k~MLBMQ!6+9I>+)P*`JTg5hfj}{6E|5{@<{`6t)dZ|+yp5h#7$&WlQ3^rpyZD~Q z(R{u7s_eH}hRnT!f8_)E`O@w4#2yj($JHRSJtGP!Hh<}(O!?g8f`MdQGE*{pMY?i^ zDwiq-q;A`F-<Q<J|K-M9n;F+uQ6?Q#as7pK5^{h#Y-!k(fD|y&BRngEc7Ac*asHs$ z7<_15-45QGrZ!^U0+gMidzWj}^vtTR76-^X%8MNY+rU0HBUrVKRL86Vz+i-*dr5P) zT0~XddHeapXxsR+k|lXQdcGsk=dyjX<7_IH<~>}Jw_WPt#8=}X3G_%!b%W3As;;#@ z4XhHLzl64e>qqw|(#e3o%sU3vZbeqmdpx&OVospGJtyJoJcu}BM%p!{BL)vB$MNj? z>iG`fwU^<!-2+EHMM$7ZZdRa^A119PL8(+WXk#?)I*zyX_lRiQ8dgkm%zn%Q7|+As z_9E1>^(mCKAUoVL_l%^+11h#DwQhiaxpb-M?IN}v^6WfNs_Mj<ZjuuxJy~XFH|!1A zWLm^_o2l{5Wm0M$^f;}fBQ_EwsGhPD2ylJ4R{pO%<?o*O3s7osK+T!v?^E&@^-Ljw zjE)4eH;M<(#H8T8@3S_cGD9+BBY1J@o_h0gTR#uT-nhuOeGKQE{sKLBm0oSSD_gf) zu^*_0LDw885>2B)PP~p>3u9<f&wPzfc5rS=a7wq$!oP=g9jXdJ6|6nrc_VB>;*U2G z$X|E-4nJo1v>MyCSUzBE`0%{o7?o3*h})pS0>8^oi?=kt%J<EBD`?9m)@3(4X?+%v zBmu3Tscg29sw1yUNKTP&^FeblN|YVjTJ!7j1DnifRjYpekv_pyi@B9B*jNF^bR3zb zo-2K%91L`&AH}zivN&7d2i6{;u^9d=oeYbfyBxaYev$9>Y%LJg_)U@+A3shLwLevp zURaQ;Vth1{rY)DF*(6RsuZ|`W*1OfGs{fdZT1tbI7u}Vy4)fJ7Us}CPYxSG|dC+t* zgEtEqb!jjQqP#fyz#Ek#KL~f)uVn~3%Nr4G(bUF9tebzSOiaNH@o)Dg@LktVGYHG4 z8jp9|AC8-^j)DWaJDg^WoLqWdF#Ttb|5u4l1_dfNE1fJG3<pE4rEK+=C2elKh!=8v z{2R|K0c$+1=~LoS*P9Wsp1%>@ez;X;`A|=btm7D`BLBYzbb{2ZsCX+es-6tSz4+(- zuE=k;vI-sCSbwiwgp8TZ4Y`YsCi>~~|4kyPSsl*&5s}eow{HPSf6Ggak*30cKZZpr z>gfLRZ&bX#qXqe-BxtsEy^(+8!u1{9or}O=K<3P8x`(pJ>ZeH_AkDz4b>}T*d1+@K zB~El{wWv=>XP?*>aiffDj=o&~*XsEnNiOpGBjp0?us%4if;~m0DrXxGA~AOS?Rag? z9=z5hjg}S%UGM|NCQ~;dIJAVVKgR3+jKF)st55VKw{tkMlJhdYSgs+;9%4>}^YxC- zTLa)+`yyIZ#G7+O`5ezaqp*ZaZ=ek2*!^!vj@W=Xp3jj>P}#5vd0!%rs*r{VW27>0 zFn`uS>>T*Rnz9O6Yd*NeU~{BoPMgVjS(43sL$Wx_;gSAJ5Io`X>$*|vC+yQ4{Qos4 zL<C6nIgiw}oKTJU)J$vSdZY}9PL6-+&;68Gr6x{UzX^ZiwfruOau!2>&>UNIh@ZNR zYn*svAD?K6xPvzkH}*@P-}V8uW{YL?^sdjK_K##KTuMGDdg?mu_Ja(oj-n#k67dD# zLQyj}pSPf+&D5TtycYd;AJcYhhTg8az67Xk#C4z?i*xOu{*rIh+1Ac*+6+AQAhW6X z-x0scW&6rI3gEQ*ZE^YIlnbtm8|LaADQJ?2ljCz<=_we}g(P>qPSBzoRCs-W@pu$0 zdT-x0weeJ#1G6p@t%9tO-komS4GSe<cb<AA+&h%eN{rp=?zcNCVW>>qw9txxOK3y$ zUZDQ1TK;6Y)+SgtlpxCwN1)sKolQ)r$JFpl!zfYoRRX1o1i&u)+XKeaK^zS!IT=;a z*E0+|nV$L$VZia_9%<^y>OE{PWY6DT#bZq9+*5%{*Y8`CQ!g@{XYt795E~8N<Fy3( zQ}h-JR~&?|if_TKhpW`%#<{lODmPZpFxveiOwcQ%LDl4JdwWwR;`^_s9QtkLS~7}{ zs5-^v8MTRF-g=GsCh_ZWf6c=Wx97JKU6Xlq&`!ni6B#i#r22P1?j3?Vl;x0TUU-mI za#aOY>bjq!+ByejJOAiuYmA-2D|;h*8M_W&4Pyg?Z*?^wLKf|q57G}Q_fk-fee)SP zHn8Ov{FNFApzW5+sZ5A~6R5NME@uSXTw!9nz;t{L&x;}OM-A7*FvZKqL-;5>0H}A9 z{S}{n7-Ih83jjnm@%DaD&PyJ(w5a`N!YoTsCRx1TK0|X=p&^MoZxdLkc6U-ecx#|Y zBcdV~5b=^58C$4GWR-W;Da70KK>?KoJ(xT{ipes=RJ88d`!XCFB5$qJKRn-S4_Z|1 zxG~{tF?hPfr;Q{Mx=w|95!MvcQO_?iT$=T{cyShYzuh=*Jz=l2cXA1WU5#YHeiao6 zD&*-)Ogg!st_&o^BA5k5{PjP<0$TMIS7=R7sRNhJXdo;-7Pq&O=-tndTzFyFu#!<b zqbLoDCUkffJRVL32G5dm)s^@-LH%*hso>0T7U)fcGEZy=p*xGO{IQg4${PIZzLO>^ zU&3~%|6~?^9Vvog1Rdlex<Wc2b}YFkIX{E#3|t~M&H!uzm)~-B>aMJuLJ2?2wdBmz z5VV?BzSZeM;rk+lPDRqzWdWa_WHA2qsw)>jr~|YOFvqOZ@Cukc#AR2GS5V=uOBHH% zu*ZvE1SOzdpvV3^Vmb<t{h~$9H5J^iohw}Fq(@v#)o|Gg7pVK{UNF2F3{EMId7XhI z$}khspoei4=8RQ3^02?+yj<#GTomS<D)Iafdnb5ycCvOdVpj+OG7nzbVKQ}u4}>R% z@AqtNS<CuG65+xj`#T2*2dlJqh&d`ca~twnN#HXw!aTD+B*=KJlj9bz9vV`y5)iE0 zW-gj4S_}w0=|ZVnja)F<AOESBoQ{$hpPQ&Q#ptV&0|mc?!lV82#c8A>{p#vQ!deqi zq5-*}8Ac7>5j3bk$7H(>(&R64VcDe;XYItWfc)DlX8@D0LJq#%Ncci!X+-HKxq?C+ zz3`3er++QYYZOO~UpqcMN1^`c9y15A=nO@Ud(oAhBzrbZZTBUVjuwi<7fSCLMJ+vr zU05B#*~}0fkdG9-q&ggD9LikL<B(d19Mc!W9D}5}kPmSP8NXG7H7tK(;^waM*fFg0 zy&O+awf=A=UAg0^?>g)zz2SJdo?_HG7fW2o;`sjS=dbwj&hpJ7US3{7t^c25HVcrz z-sptmoz--eD~&ct&WaV}DP#1B3hVXcS>MSrZjHQAZV5rEfluL4ft+T@Ta!e8;Z}zl zedUpE^O4^x<rM9i_E9wSM;K8!G30a%0Mpg<M&N$yj&g>d`#bD|-g!TBeWEyxq9omP z%;w0;!96!}9fs=Y27spKYgs?7-7HVQVS-++7wC(=2!!N}{^_PE#Qz@PtFs&2a+{?I zEb`a9s0Y{RXT^zYrs^KNK1DzH{255JfKn<1nHQ?SvQ-uz9!aIj8MKHI05k~03?XN# zA*QB*3fw8&Ss_8qyvn9dakEII1dDD_)RGR|(K~2qwchUDw5Uffo!OQQL6<@>KDu$q z+_V=2pf3b2ru1j@q=Du`H7#hdB8T<)0m{d<*AxgFn7t0d(-iu8ZFv?*r7q>p$=pIM zb!xm0{ALy=t;LXi6-rOiz_1GkPTp1>K_$-2)tbFBN@cJRI=;z#F4g3|wrJL{_$Va5 ztROZtx{S+N7dP|=v-(u*BmJd}yX!=zudBEBr8oY!j^sa!bVeR=r*RC@9zkO1#|#^- zFH>KTN4;GJX4ykN__pjl6sCo7e73?hNWK|7Eyex9N&PC{46n+(<hLG`dVFenz!o*B zW4ZV581#eLipK|El#(zr3|#cIs9|lJFq$ter7!3RhFcp&`j6HE-=q`{y(jJY0&`Kv z5LPMCJS6nwu$4FG%y*i!trn(CrYxjF$^31$IU|p49=ouN?lxo*)bZt3!~}hT$ms2^ z@~HT)i6r&Iw#h+T!8?+gB1!r$K>#gCgYNcpitF}|&zV#n=5ynl19~+PG#$=yOTILo z`cpU!l%oXG5xe1XevvKbXePP-Mv2%7Fz%Ef2GGD%sX^KMwn8U|=`u-NtE_&@0a3XW z(~>5mFJWA0`pu>4k(-u!()D{Ly+Q0gw&@f?NmGoFHc!4`{&P_sG#1$l-C@v?>QYa` zm0U>)y?#vV#Ym~Bx-_n8GLJ)x6itix@AJjqv8#<ydcI}z*l!|>P3H!fVDb-wHv_BF z-xjQDGa{!s#<YKBf#;9yKkC}Fw~<73)A^I>iJH(fJ;N@Jo|-X4Hj$UC-J2vbwlr3* zgWspf!`D9DW@IF;7`n{pZdq(SKfar-jIg2i4EX&?f4fgpsxseUY@qydxZxv>bkr=o zV%<j#c_pExFX3=>YTEBr9<S_nS<z_DebuuSzXbjSk+1IjEZkOX#eTkNx9}}j_m!9T z1q0<k_wA$EPv!)bqSduwuz{<^`9_SI>J>NFJmfpBJD*$OD5Vmb3#U4aVPok=;Hsnn z>1F(gXyfW>0vE4|S~&G52jiG)q`~E93nuWslRvt5CnV42KN|Kh30ms6>^s^sX^+So ziT6J{94*+G>hyC&;xFYM2ptUCsBQhZ-PPXN@LhSGxZco~rZ*(`43T_K&OKM@Ltp&B z`Vjl6=l@@LPqL*+(J%4Tuyu7;*-X5Kq2E5?3(qaN7K6t9!L%G7DT22gw5s{oLE9Il z6|&&AK>h-*?CyPvXDgLA)~}=p_Slmiy?GSd88MF57b%m<U8EbY@1XM;jVPL!WFD<A z)_xTwbo}DNU@w?#de}@RWc|!M#WdNm2(TK|(G!6eI+*+>?>8}%r9!TLF0ZXeQnzOv zY0zdpS5{`nhWG743l0+*69p(Ev?!Lv{m^Ifwq+y2%nYUyz}ck-r!`J#^mePq-rC=K zrO#`yU1PD_-Ryd%zE@cb2MK(0J4bu`_+b4Qtf^Ws)x1T>XUaz_EJYwobjIZ4xjcuU zSPtk<&$}AJSU9s%?f;Hcr%p(^L0(+>47~*4B!(RE2`7;}{r#ex-`SaU_`XMJfJtmP z@j$Q$2<By`vGNfbG-d<?qFn~j`q+iwxPEry_8|@9nX-Onge+u*%)C+eCnwC>cEKzk zF`lh$NaxVXV=1<}!L6Zk+eP<`C`$lnAduq6Cn^c4>8%qH5ll}!F|u;})AU}hKAb2L zHgy6qIuvzLo2tqg1_q7S6Khp#C}0^76*4mOJ>J6{!T{c#aT7}aU!2k$Q$r9X+OsPw z8Wv1z6G~nhAve66>f&A&b}5c?oxnh^^%d|JQ0coGO>y1g^zvBzw5O!#&e5;=G#Q$@ zJ-aRjNJXg1(7<WGaek@;j!Uo0eyEUyuuV{KT>%7-w)#-*urhQxX$R{$w4>wkDzKlF z^-Mr0uz58KPf4NqqgO_LBhDpE?0Pt}`zzeP5GYLUspd)F;OGk3t(ghpN3FrTW(1R+ zpTmyEdC>eX4##o=tZa$q1C=USG{<UwDJrTS@9bABuBSj;O@l3)gD|c)uBQP#MKRTv zl)d%eCq%EXHDB?wfSX6SrNbBd{4%zmp`TeK)<3&wyHJ?vE7e%8F15B|9*7L>=ptIR zMdZ``F3lR`1D6#uzQ<IyY=e2M<hG=bmMxG3F$AD0Rnwf@9h2JnG48p3x&fo_HtFWk zj{G@<-UV<fsjr=`t38sv>}|Fssp755admJ1c_2uV34MQow@>pg%Cv$atbVhCS)&sm zt$4r_=*ED#q!ohcW%vFQTZx|`>|Con=$)h{B1t8_vgb!i{9N0t+LJp-%L=87fjK2g zwf)p}LnM7aMgjq62B5b4Ur6byiOZm-GrMS8ZNq<1QmP@%Rsyp?Ca|}xZR=ac4B}|U zD|hF#ro@}opTE?>al|Te^_ki1qG5W^$JX?@xij8IBS|8-2Nx>H9(BL3#J4hrW4<4? z%mi{m2ucE~PNV73w<(N$Pr@ZrgU`>q1#M&lKGpujk4eD~Q6^!J;lh9Bj8y>C;$4h> z9}z@;ai;=8E8|IR$fx2?z1h6|l+ymBw#++diI7-p#6%pjLBM`}-#_3|?4zF7Bweo{ zC#pSTwn7nDbX{6LiDc~7b&>bw#Q=APTf#R@YAYHS)zI%RSt?cZR)j;4(Kb9_s_|!t zC`eM5rW0hZNO*(9MIJ9#PrR5F5A6h9tkf6^IpoZ%)<w%=ZfsLhkF_mrimGh!C#ss6 zlf=QdAVOnfyI{?xe+(Mgqz#R2S6we~@WCZ#-|^Nl5!!isQoEjkyszWX(=N)GIaW`$ z9V_qua_hsOpe#Ka=^{kz{|$V0%*5uBJx{D(F8@}2sk4D3bM*&ak7yF6@bL5V!XxAo z$d^)EahhmHMcrt)?0oHR+b&QRXHq^N*}ECIa)d6O;p?zW!T8JiqI!zB^*XyChySij zTlvcPO8AIZUesZxrvVrA#O5{Z*GE_9?wehT?a;@EP>Y9nMq3B-t5zvZn;q-j^!GX| zMjcu<@|z=^0XoBxy(834&XgD}rY-a>7@0%6(*cGhQA}nsIe6)K4_<d3cZT;~w>P!Z zx7J0I9R<SRsyDm6-!(EVHKT$KHRLYV>>qSHz+<2AibRXCy`)Ri-Z<c86KL>i_{jRD zMW2vm+?`(c`^Wik-4Ysf%G{6^!iveYOOUS{Z()~3Sk>OsrzLNYN78A?WZDU+=ct#Q zXKAAeNMT$O1ZXoj#Mtv;6I)dhs^DKHid8|sXnX$m>G{K5Kd6K2EIM&%zh~dQP{c4e z?jWeUST7Q-SEF8}Olw%zV|%gSlV_nsKRa<~dB}_OdVW3mikET;*5g*m(TQz)CFZe1 zpW0L9%Y%HG9_vsZg1xi98?}3%>3u&xWFH|nR3coXG>BokiFaV0;TT(y*#73~{p@?! z+56#Tl7OD@%G!iqL+Ji@e5E3W0WVXq?c8=F5T!vmCWeg>=2GU1E+0a_SLA5Kb+^Xo zdtZJ^LuEP2c|`d5`nvu6=xTS8L!Q{dwDY4iOwJ>7CJ)Hv|GX{oXUe5h3tr6buqfSn z+eyqiT8|M}?Jb@|+v?sVZ2`2`hugaeo7RdVAlR^M-Euo`I0CwJw6pC?j`@}(CA30j zYdzsCMv#V9nKqB^Ozt-B#*i2h=4xVu9P_+KO_^LQMcl~FgmI7>uCmDtU1)QkJf&iH z>u0r+l)6{~=K%Bo18<%Sh%JaQr!urBuq@y^o8jNG9<BJdqNIbQB7G4dG22F^vHhWX zbXhn?336{ne*fIeBwT@3jqI9!eZ8%I@fj7$c(Qo&Q-15K0+Cp**ot@H{q|-=yw2M( zm$KS5L%31_m*Yz9nX4nd=a`qfCwEvdNyc$N6VeAuLORC7Z=SK|y5$`<+npw`Fy;Zb zX?}Xz5pgy#^rZlk$Fy{dIfhLGC_HuJp2J11<fHJ+v@iK$VqM6pG`<L{%ieE4x<zpv z?V@@l!>P|THLpTN;Cj+y+u%Tzo1;@(F$>Ap-lwX))G5`Hs(=>Mm_9l6zW}bz5pAVZ z>Wjfj275!T%50UF$WXC^ri~^z^eI?5`ri8lHAzb9cDi6jPHpOrPIYyD4(jqmxpnd^ zA%b8k^iIhtv0n!8yZoV4u;=ci6UVsHB;5@QHKcMgS;NfsWVUO;MyaFxpx7;MFU;QZ z!<k5`EwqK_2{x<7=?=+vq{}Uxt@;o;Jw$#%O`kCR)NR5M6$A(mDO{0}+kI}6L-#Jd z-#Pt)l{A$-|LqdGIzb+!a!VtFU}rUC>M@A2+OM<5LT~nTvnFR@opCcF&c-DbHMWah zRccHAAt}7}lvl2=vP)7`XjJ;02*b*h=1d${zarml{ehbw?qFF`)Xzucn|o|;(j8eD zg&9Q(e3eN5H{$44@s7Olhv=5s{2g`laWDG?`;p4MIBsjW0Ux$;vCI$bF@>Ij5KL?N zrChVD3&V5tC{8#Fb69ZVd%JrjzCGL&T#`&rt=+V7^;SkJumF;=$>5{V3?L=vVDmRX zw)nES24fIs=!^09&|y!_`TJMRZ|91-<aW<;Y8{7K`6tEOZs>Q|cHw(_ApM$|L{p8h z0j_5*b(BmHpEm6qhs?>!cb<0iVzIV)Rh5sq48cv?sT}i3E}w7CPS-Lv9)E>Y%Yc`& zD2HYF8h_UQG((1!HQD%yq6ReG*9GBkC~j+vl;MzFM%$ACZeRgLKf9?qzKFt_OCPsB zvZaG(c3pO^KmiR4+s<EFW3pm`xJiLp=SnBiqgMOFJKmwUl(n4BSWxn{t!-~Q>}hgG zF7}A=-{WkmGhrUF-m*Ww2>d~+AGagtFtSVoMKmYAFTWaRX~v5I4U^gwIwozdTKUZ) zeY63UP1~Xw3OJvm3Z>qqr$ln)=gR`EIWj^m4f?ngZ(H*M^ae|?^KaDqeEKeAIjpku zE(b&hgLxn2A-lmB7Lz^9Ewl;P{QI>fE5WG42;2U(yxNV@P2)HJe`WjqH_m*Oi(#Jp zbS>m8lkv(QO~^?T>JvmTiWGeGy#m2phJ+85CqsfG-#s?OrvA?+F>*Nzt=TVGT+j|v zAbmo^9wMpb@~6VJ<@2Q35mTi3u?OTqSx$*_FkMdyYb$?X-Q5h9Mq?<J547l3QsZY9 zchiBe82{>|@(B!uB=qGe-l-4E1^FNF`$$cLgYLB~SKCLFP8kENx-mJYcZ(<idcLgF z{NeUvBHzzuKEn3w6`EAZ#f(-Jwe1Z4G(i5tt(pe<ItLmp_w#^bV%HX9HPNT-H>RaW zEiz5#q_K|?D4!o-A$~6!|7J4?@khBq0fMzI8J+)Tw7_%|{mUlvWcM$?;9si(fkZsh zAe5c`XjYSj=}&_Z2Jp7r@Bc=(p1H^T!<JEi%Ad#_8vRA<<==7x`j+p$=^jA%U3@ru zj%@R=jH74&T7^?5W|Wh*z&y~{sLbnezuPDY9GLSgteB)<x?0fo_Vwl~*}rvDITnw3 zEHpCm1_m*|5^xIBqg&2Mft(o9VY+do{Aj)_qk)Z4AC>7H0?HPaMA4nlFbsBBzi|E8 zC^6k2JN)j%6q<`13ds2P%@oB=Drh-i{pyC8ZMny=ORbxfkR@NQbW(fW(B@DDYQ6tL z{%gS#+HyfPVS0n&hR3y#r)TM`+OcTnCb<mIDcUNw&y7pyp#oZSQB-xso8lq9d4OA3 z^2;dt{DSNJ&>8~=RSwGN0hAIuC_LQK;Wecu0oQhp-2V(I503xczz0_za4Q4cXmv?D zxcGqMBekX>?E4g}+7meL0b95Oei$kv@s|}r*c-29+h9q(7`#ob#v@H*R!<BPZpp7( zu*Md6aV3D*@S|a@Pa}XFdO_hiJ`w_KGBPqx)k%U$3b~)ZTC(vm0t$ew%hbKBQM50A z`~ik!qUht_{zAeV!C0lYzKZ+Ig$F(dYZ;jUE}Q++9w^`8az8e`MG>Q6zfoB?<2NOI zxCXiCW^hX1wm}+K)F6hg!s0L0uu=9~Pp=W-?7=27>JZWcz-0g4k5=nx0q7&Wsm+AR z*kM$!8s374FWG04FQT7kg5eD!Y6Iia?~9q|Ue1Mmm0<N+E%|63P~jNx2LCHF4dL3A zwpQHJO-@R^y}X@_&}g#DvrB=|VS9?|B1j<ZzwHx%G)FoI&kx9mxP2mNrgB!B`#c(N z1x4=&eMN=7+W&R0|C!V^rY1*@4sm#RH2!f1K!Y*G@ps77HW{hw7a@KAM)?I%BNk<i z(5-IeJrO_Jrd!{o<7BUyx<}#IaQr3SjPC1lrsZ;M!v(|o>OH_~RU@gnKvbd9i8{S^ zSVF1})EB)^c4)4yGke~y?rvwOQ0mYGQW(JvMPB~wVqj+HKdzJo;!@q7H?(;%1)9u$ zDE|SmYz;b?zz1DYW!RI11oCK~a{o0~*G6(s_|%9`qF6i&=XFZ3hn@-He>@;q_Ak>j zC9j)cK3f`8)O4MiUxqKkGL#Dnbo2|6D}7_BzRJokoY9x_d5zt5X|#v)QmC75AoIJp z^etWT8RWtzFZ;r+SA-6yvYFXqPuYP;O9V7t@Pr1wH1q_ij5%pMrgsLeZ;rKG?`Ibm zaoI=s!sTQtmorsDa%k$7T!R*>MNoV6_UO)ueP4MKc&?x}sN1Uwua(xlc8n%loNg3x z1)@j3?V##Ngr5EO`FdrIxdSunE8|Vl`f~Oqs7=wv(9R#*STEUBL>C0*hz|#`hN;SE z+f3jwew9UOy)MnHWI{z8g@Rp9!a{5Bg6@>U>>5vkEN#HgybACTusuzZ5;f@aN9bEY z9swYJZrNwhXC^{KrfV56k8j!K-kKN2WTd^?)RoO?2Dp=}r)_B}sM*K`vz|~<@k zvzw2H8D8WAT*)6;=jEv;q=90E{loX6bcDc${l3k3D%CNapsgB2phr#6vd(RlFog&Y z+y7N`p&b<LW_H)__LxI&d69Op3KgGOw}@5H-0cH@29LCEa%l5R@PNsy^N}0e_6V!F zd~wfQ-7)}SylA8tuQ1Bw(oNHGTCE}rA_|af<w>5v`mca{cE+~Xv!ldog)kRCSIkbw zm2t-f4D&K$e(>8%*v>>0CL8YOux{>k2~6KXnK>BIG0=(1{0&Ydb>yoKegrquCtAp# zW$jKj_T&8l8kp{oh3V?u|05fN%#q%Mx>ek3^x;p#I5DK<95usMW+>^O0d@ZT{M>Hr z&xiU~W~J!4SKidz4r5&KROcK4M>qG_^2Lwy$L|BLlr#PHF2H*pa!zGR93Eh?qS>qs zyjd!$fG0I*h_h$4-CGjPs3PFx)@VzUNv%jNxNQo4OVfnhUD90=DOOH%Fn0OU^`t;m zn0F+cPQeG>rH%~qT5fJ;2>-?tWky4dZAY{VIuZ(1anZ9U*$9<<@R_#x9Hphao0qfQ zI?@A6fHi`tr#>Mv7{%3zdTT_gh4Ew5R-rhA36D!)HyAZcA5mikm)x74(xEx7ljY!V z$mo=d%AnQc9r?Q1kkqYudD!p0^{uiSQCyM?H)~jtgJ^^2gp0gsxuXK5PYZAizfPOn zQWDFLcmaJ#yjyDa+mE)OuUr@1P`(9F<2xU7REe)_txhw959nzp+o8>p#~pG6(2P6U z4ObCq&ei_bswWsMssOK0tjHF$yfe15{7^?(kpuS_-CP*{nI;i-t6PiZyx&Gu)LbGQ z{+y@cp<0xQ)zV?HhMS$2JN8+|wTno9@(Cuy!dPQyy|Bld@{Q&7SSGz|rJ{YMksG0w z=q$4s@@I$7T)0J<j6)Jba6HF!^7V!;Z0E|Q?nVNT^vDGqyp1`kyazg`AGMUfVm5(9 z`DaYi8QahoQ<t+^A<|wWdS8R+J){dVD|2bHCoa_OF%Y0R@a8Oq+M_KX^eRQV(tNi9 zW@_-j&R&n5eIG6D6eKLck1Y?uTzXTF(r~!vU!zKCa2kjblnWq9`amR5DL`PQ8<BKg zQdHZF@l&DFy|TM^_0MsG-jnm+UIZIas#skkf(3{yn@F~ewN{RCGS8*Vyh5giTS>bG z(7A^Kt^M>PmB{c|C(F<c%k|zA;pVBRj{rUJutf|c-zWpT4<Kh8DUSinBOi$3oc5%> zxRR7;_Nmg$3*SlDCu78y>8Bfr<2U_kGH$AoUtMfZELN6Noh+4fk=u{k;TU_Z>Ev78 zStz&){iv{x-;7<9Qi^9vfu`5B=TH**EHll>YY(%Ozm(bdzV&zeX^e*W^K5+x`56AQ z(t4r0wb$3BE7ABx-t9R>iQ6VI;pklZ5X})lxC3fzu_xN3h<(*OHEsmr+r>K$oT!ZG zVJ@GL^gb@7FrOKO@xt`vma?3+M8aTF1C**XFY1$s6MmCSOEnD#1$op}Y~%`iu?g+y zt>gu>vu8~K`W<Jl&$qnzdo0enc}i*mak~j{OTIWU?NyQ|Y`K6+^XBDC@ETrS)I1}* zJGyS!puy~;`QgMtRen&i$y=I_I}^j^^~w-{2cI!BfZ*O`TorBK5*(Kt1`KNv%->Wy z&E-<M+iaT-9pXDIn_SJq%b=}40S*M+@|zw^CJLX;53F1%qh;T8Huf7q?aqyNiBL@l zywaz|(TbC&rsiq%AkEXiOUY>OHlPg(fXrmo>e#}0#(DA!wND?x=eeTIM@#4sFXIa` zukUbd%0)bN1!-TO<A<mj*AE4iEY=Q4jxEGzozo+2shg)U1G2UvS#uG&QH)mF(a)?{ z9b*^kB`=@Mb{wc3Izewwfwh-MVNQoWw2O8~QF&Q~l{XqUEjGY)XWx}3q=uqBG!xt% zr((LKKfVAM`|X}>;0@RKN=S>w(rumOlD_QZ`>l!O5KN^giz0rE1U2Jjr!Gzd*y8a~ z_k{Tekr;lRD2epy#A$mZ6<+@_#TcJI3NVfJzkOAp;jCEo6o1(@Oz<irctFbOpByJ_ z8BllbJMN}sCo6IllF7sB8wLUsM^+8nyf)zqUnhlf^i@O%dpOn}uj}H-`u(l2CWi_G zDr1P0nna|mZB>1(FFw$9EXY5G-RH`jaFUwv?iruc(gv-~j*;NVCgver7|>S}jw37p zjFr~Ycg4?p#nN^M&(WOtCX(<It&Lwc2v9HbB53qODCF@Yi*da{sWswd8+)q@oT^bg zf^8MwdNL&EC@0DGf~`2S$#R1dg7K2N8V8XcIUc6&e*{Mx8I?Fqd&>uI?H&kzbQO7; zd6!Ce#64J-e8NqbcW6bh5i=~AvV!ZKaw*jeGkW3w0KeE!&|EVm)*HufF{|EAYpyT< zZLYg5+}LVg9aGr?AcN_%lWj9XEaa~4DxhOsSrN+Gql)|TUhs^yUX1|;V?Mge5O?T! z+uYZNO}!rrpH@4S+1A%(kT(^o^Wb6z_SXls#gWH$7P+H%=7A$(y|WZ=&E~x<u83Yp zdGbi?5{cCx#@RFx7#{@R91g62MATD?)gJ6x_{v6yGwq<k-9g_L-ia}|uU;VF>l=X| z1dPlQmHyM_y;R@R(zOZXhY%@-JY=uRpULWb!=$fE)(cA}r|kiw_zrp19$#J07ZW^g z5MJ49N1Z3G92f3)n^dlBGV8maH=&sxW?L0h2LRe;I*&}SVw=Ri^Qfr}HB2_s(4hL5 zrI#+QR)CkbD)#2N(Y1E!RiTYt^Z0a4hTm*NaA9z0OGg=O0@~6x);194(a(a=b#G~C zs6R0|dhGnj+-IEOg{?Q~mRBwO{=VtbA=i<~5@iFb`H?1*?)K+ovxfXXmn3f&G`8ZP zk}N13XC_0X1)V6R$gJ2p5?{`V-o+9yQ;#5`@cMf7+;pO;&GpyBE^?kp+wNBC+JP4X zV4;`O3Gi-a!0<;9owLu2l<(d&;hbnKgmq+1HU2}#ffU{WhZRT7C{)NlVOG<~iS)Vn ztgZLC3#M0H=FSc^jRz6w7>$pW3sPWSlYHD_J!^IZSn1Ri9?jdD713iuzY^MmG(tsx z2pt0fBfVr$n0~hTwb`_ewZC2=LMBr^;GInb!PDpEr(}2YQ8~kqWTfQf0(1k^0`Q`X z6@Ns_U}*j9<N=Ea7u-yo_NT;u;if@}If^NYNswttLRG18SAH3(ghOLo2*EV$6bWfT zL~fWRNB%s}OxKleUQ^{$7f~0KB0w8MqbX4iec>61ve??^T<*kiR_TSRT(kbZ11<ld zgVd^IKq5X3W7_V-kUpPxFK}`gIz4b$WAsF<QG!AuR(|!gy)OcAy*fU-1a9s&Y62); z;G~fA01nAJ`q1>slcb>z7im@nfCd+@;D+tY3!kwT9tByW9IvT)6#uPL0=_1jq_;`9 zZ)y%q=IX(hnr6CONVH<Xxp={6b^DW_R4{Eb>>PyI3_v(}q>9LP*<*B7@<X|PARzcx zt0~1RlFxIwftvVkDMzi`(0jAx(yPMl=AA{)pZ0gLLG#y%R|Ns@Z=cpuFqZl(`rN`~ zdq!hc57kBm1nggZ>K-jLJFn&GU|iq-93?2J;J5@^_qhTsz=eY5KQQdSdDa8`>#*&s zxyz9&eTc-i{gm!1S8cRqG@-qZVi;FikO)NswT0*y-Iu1>-_I-Rw|>89oJ$lliN?8< zAGPD5BSTy@5*XUjU9Dr(&-ntD+QN0uF$;8Y8E?6V3XV0VT$xi^GmKyFu8wj;>@t*1 z!#9%qhO|pT>-<rW^_T1|uW5}JiGRxsE~nFBArfUBQ3(>KR;PNU4#}n!OzZz>C4D<@ z<%k(kpr{{>A?E?HhAXow%qC{q$eX=S>NiSX^u{b@czNUl#JXd?3%I=sZR-weK#mH= z71=*(9dkw5&34!g73I6PAvcgkzPTI7Db8)_MzSOp3Tm{QWLEEW%FzExfy{qcGdSl> z)E}Er#=(FSgxkCYY`~B@Ze?Dx>4yAbal^|WIea{cn_XC({vG`Jtn|x<q^O|u2s?UO z%F(YQ;>mO~hu1^f(09If4HM06;JvcFnKlLC_8%SkpuN0Kb|q*fgLH<G@Rd@qN4d*y zBU-&#!s?StYr#c7whp!|VL~t}lpr^RQiH}N+%nrT^2{jT;I&)*8Wnhb<f_euZpL`1 zX)X}o75Wa5vO}_%4&9zVYB%)0hps`sIPPw+({JM&MzrYid(d7{OQ|~AG;#P0Z`pRU zSUW8+bwt)}vFu=?Uvat1Lry<mUCX5-nPO(MYy~Ozoh8l$W&VxH7iuC)CV%hq-h|Ro z{{XI>O&JSanDEpzOZZvw+4sulF#&(=fG6NTu5X92^*Hw{M!OOV(hDqNC&71z*EX#Z zQ}ZW}6+RgjuZ^7mJ&%Ho+}zEga*piLZ_CUn0$@DTo$2WYzj7(F#yUw=KLhzII;jI- zZz;%#4qm)Q%}%tMOZ#~R@*~a=9ec{!0z2zGyKVR48Q{2D^zh#1^H6`#`B32Jr*^>c zf=7iRA(*I_^=eK%9{46E`ON+3*@vHQ;@s9OaSwc>OvijW&J?G5y~O1rT5v9H#yMuI z7pL5qNfc1hV<h{bqWbD?qGQ(+*HbZkHC`d{Szi)dLw-#fK8z;$W&t1#s4B8%9NQeD zC<o8n(K^ta0?iaH-J_{c&KIP#i#HJN$jmJBNzzMY{RPL@7BGMz?7_)I!Gy$>dQYfz zjHb{^XNle(BZpSS=_F(oRwQ!rZf)VE6=P1DNn@kjsE$4Hycj-Q{EB170Ts83hOc}@ z;`rs&-0BieRaq6w{J7bJbRWx2<wl`7e6*_GmO`j`-cx6tOmlBti{y?Z^We(LK0zEg zsH9nDhYYhsqWah&m^vH5+;&Rhtsk`KKA;+(9hw&#Rj=?;@{FxjOWNicDy*QqMBeMz z{agY{g);8DoUj}SKP_=#uPkwR2uz3HT$^6HfzqS=t9CD$&#C6mhSGK0ZpgNe)CIB! zb{jsQn7dW<CTJouK|k06(LYajpXO3-qskHf=Suex_}L`KBin&5Nkk~5LkdN{*+5SB zUAx&CMD?l1_z3#plQ;Q(h|K3btT&P}XtAj-F;vOWr{s|#f7@*Hp@{yFe`Fq5`V*(B zYhFNen4>~PioJ0v%YKp>0#lQ55=(6fWIb3!>g^l4k@CTq+||z0D<P1B6+%i^@S@Db z8`hH~7lW_7`wr0wgCzpBvxF#x?4bkqLz?1G-s!zyH1?OV9^M@OO70(`Wa#kWCuO6k z_mjntla}3JJX_(s%>|xF`IW<<{tF58J=vTr2{djm9yOjrSzwNymP@$5+^5&Pq3+uC zC5vcIAM7XTn@M)*uj(-R*)Oa%$uy}sy`u$Pql0Jz-~F;yeChr2jTi&<q5CS=G5aa| zNfH6T<x=z_h2Wxg#v|VdPoT&~ZwQVO=TKPt9evP7STz_D=oRb#LJ~!#>3jAfhHf7I z>nMdj_;)TA{PK&ELw?C_`Nf)R<P*s-l)?$5ap@)*9v<Xjr<i5$Ra#q^U1b$-m~W{Y zox97H-as01fbnJSAr1<H3_~?l0bHv+@*BuKnq!g2Ac-IKeJlaS)8)GMu)CL+0@}-O zC?I3VAkZ)>J^d}A?stpT!~F5xcR8x;mh8_cih3RK*5Jc+TpK2jUXX_1(yDjI`z)Vj zP(O)0tL|y2$iuf!PkKPZ*{a_)?t+{8p8i15WG#Ska>;V!y=R}2^_Wa`yFK&s_vu{H zzDj7_>_`MyX0>%q<<xti;~K9#8bcyR^MYYRz-`&r_w8ef3>_bx*lN<}arP(=$-D|F z(b4`lwZ_XAc&8c0v?rV+7VZ6u$k=<PP=#+Y7A&^A<{i~QUGCm*@1C<?RNM>+@oJ*v zpo^$Mt5;k6@b(lcG`f<RHksD_^@a`rn(wrmn#4$Y8DYFj;#(1d&&*vaXsox9P05yc z>#E<ApT_Ut)MmQCK}Xw5a>@P5^UuZnx(cjbU?iRYT=#<*fnT+!#V~%^Qj82e!OMEP zh?~$sUc^Gm&HDyYneLn2Vz0wYda$*iG|$PH@ilP(r;XIc-T{tXCG2P}xU({SF&Pmy z07ztc2555*U(%5TY<Tw3B~>E78dhXhn**m$4m%_w=^MMDDe%xprii(U@0`Y{+JK2J zs$F|y1~`655<1q>pi?hq)|K64&yi0y+9&K`UygN~cB8OMn;BPL12j!_R`e-Q@Wr4= zHI{gCkx#!aCzyVR%e&E>)ouwfCV%Rb`rfMkJuMp$WqjLp$<rp@@0WK*-wTWn;1MM$ zo+&Aq=KiX>@#F@bG><}*^8p2S3ioAXbu6zLc3cw+Mfu8GN82X!uKj30bWhklYgEBy zZRY(+4c&rJ#M8WNw>}ovU9DI1n-h#fap<bN@;zaPKD9>Z>D$0Ezsrc6Zv%K-HKFxZ zeHbjta@0MxN}&~zCk<{Qb<f;WR_6&-S_FAHsIcp;&P@BbFcOedRR?StL7zhcvM=1& z&NI06{TY|yiPK%BQr)_#9A)?>1(W2Wzf!-*)RbCmnleUNT*ZHhuv(E^D7W!XS%fL5 zmH&Eg*dr^q9!?dJ*IHAbIa+>&)pM4&Wa`9>;OKB-%<0q5nK1X}q_<keo)`aBf4zOC zv!|fZ06Kiz7+gaS)#~W#-oz-^`yE$}8-BNTM~+4U-iSt1Es>$O`7<|BS2Lt7L=L+y zm=}c)w>pg~YI8&VUw_dS%;><XwAK=)u}Qivm>Fy+le6cF6)_!#inlj@yJ7<y{qmT& z3Ev1KTA!FHvwGW(+OLYxWEtqW)n&dth<H{&Ae?rh=d>AgAr<s5%J@}NPOh;8S0AI6 z6*CHCa7;iFBZrgZ;cU8^{XU?kVT58V8eW>^7<b?5TyTQ1zVF&(F_}$Ly}FU6ZKHk* zvMv+1Yk3kA&WQQfb<7ZSufdQ0#J8@l{^SPleQAz$^{X%+mv_}MK;%8PIjRO!@W|ne z(<LA(r}EU%oJ0T)(HyhTDV7D4l){0dO7~*|gt!WmFV3ESohSSi#7KN56JI^M=kg_# zXKVrPY=+(zFi^(zNPq2kF23o7H`qMrpfW8xezD=*m$<q-7%*JG0p^)nt5$mw{I^!E z$i{|w%!X}zpu+m#ug=UL@7oO`(Nmz8l~K7rPkGP@K)=w%XaO`l8WWrY7|pUbL-+)( z2hGQfIE@<Q7o&?{CTMGQ^E-Pe(CvG^uzFcZB7y%7l-#vTR*yWTq#W0V0AARq#{+x^ z!SXiD=`^iWXC8yA7eKtA2akoKrvj#hsEc{RKF`~+|JO1`DF^=sV#TK~@G1FDV<8>I zjAqn4OOaRs#%Y{l;c^x>HzSW83n?-nt<&e4Z0jl>-^%}17;_|<{--=OGWGCC2eq{! ztM_%s(hSR|BV?p+ftL)6Q0|&lywvC9yPSW92Qf~^2Ym+}A2pC~R*$~?y{hnIsQ}-Z z@!Kba|G109X!(EGdaJNFx~*Nik;W}SgA;-W3GUDkf(8ig65KmDG_FYq7A$xZ+(~eE zcWvC=-I|8Y{?~fHwf6P>XLZomtm>+obB-G08P7fV3GbWae~mzkfh_7nP)YpKT1HOp zHSK@@yyM~{W8{}#0H6r(3xfaLAp+F3yS(0TnO&q;mA|+M|7UN1KFIL!#=^`Nf;#Jl zY`my0kRgsK?@!t)r9HCXr$O@0$*fWE6xQO^7}Mj)=!a^l6&I!lTL1n=XiEBDgWNvl zSrPCE0)NuS@*@4tIC*v{EN(GdLV5w;guUeq*syYhH-!a?PFco0y0yKKO?#9A0*PYq z_w5oBW@xg&Uo7Q5kXDqu-LpwXB{Jzi22$nN{$;a|s7T(~#Z5GqBBOh$_3vQh|DJSB zcQUS@VhWkp(S!+E4X$YSYLe-+<Llu?(n!$Ao?jCQs=fKk;HRGNX<wNzFqjNdvFE=y z#Mcn5u2EUP`V@9R&Az${KOU440gZt7;v}k>q%r8?QS2+)k?MxFti15E%Dx$BU!xVa z5WNl!aI1EftVdehqAq+A>b+^rO%FQtSNQM&ZAszI><%}EvEOMxy507#w|*)BzwgaO zE0lsh*Hleqtb3pTATcO+jNCdB!0}T0@B-qQi_w9Je)MeKhN4qpaz_Z9H3`O54NjRt z9eLD<`u7BW2;Muh5NAS4&Z~Nf6uT(5{km`5Hs~krgxY-3x6!(Z_c2gdtKlufY6%cf z`s=JqR<il52m4U^2TRYeYPHL55$j2iF>Whhg$5w-;hZ`rQXUc`j>Dn-wbUn<n24J7 zFN-biA{@f_oxIe3w!Yo#lze--j|Zj3+v%kG0#D~e?++SchjQg9(H*PfiQ%rINyP|1 zw!4(IdJ*lIcrmO#A?PYOb=y%KFyk_Vt_7*kEzZn2K}zJC(J6jarKp$YP6cj%c4-=& zY5skH@=SVGJZjS$oSzncDaVxl!)hfuMq7GD(;H>qprkrS3!~(q!gQdAChL}-XB$g} z?B+zSHs?TPcVz`3RH-u0b`+=p4_?7&!E#@jkNm@i{jThf`e%_qZ_Qyf{|$*ZHQ~65 zI2&1((d66Y)wC>}-4b3ni<OH987V;27w>5Wsa?sA2vAMMO^1ua0zHbfft)D6^hhJQ zWTbzy^t0ayF1~GXdz`VZDAROj8E>7+7ReXM(Z_GsB}N`>MFZtcm<A5FY1KqlC#?DP z!XIr|z;}xYlV2*^%(+8TdXVckW0^*n{x!wcXq?l#ZkoD5V^~~jZ;;OgbqVd!y(!in zsB5`3KXlx2J02BL-VJ9S6;lRBWcZcOB;)nNQUvjKky%PpH7LfYIPlI8Ej7qH`E`w< zWx)r<$f+~>X!8ntY={Agaaw)*cL7PMFfA1(vR?viEl;}EnuS}n?8m`RpIQz5j025d zwYvCSKtr><Oe#UA9eIIP_b}j!n>ehw81Uf-*^{@AXoo0cJw9sY<1(yy1zAW}oZbmB zDiZa?enb@VL>ewsk`8d7cB$6(^Jb^=ak?G481IC><Fuz#B@W_Gq}IA$q^Ef!uw{3y zYh9UtWf%Kw?DeH_&4De5L(fC17V%VeH{y|=CzOExbpMEs@P5u%mwi9RUlc+>5by=x z!r|#{kcq+SkQ$Bj!A7iOsblbSS>$F>ibi5NTnmRvi-9Obgr9Sz3YF4Ia8~5-&W5rU z#h7$uOPqy^*%W#Z@M6r>X``0>1kG5229kN9dUo|)lXW@h*sZ}~f8v#}&$u{r$*4l8 z*f`gp)HQCi-5K(=WZc;$B5{npWN=`FidKiTNywA(HsRWC+bV=)DHXzR2S6O&+wUG> zMCnH9QyRU<kN(#LJRN<l{v!}WQW`G8sQg&?(t`}*i4*3r(BOpsw?kxSF+14y>3(PG zZ$_9i!qm#$PEmk9jSTj%b^TazoS<N4TNT_dK7xYyZL+UTZ8cC(@kKg0RC7DfTcx&F z=OnsTcI5-<Zyu@5kVH*=vbK#Pc)p`eg}`UyP6U;SSGxd{mU0*|RqL677n=L<;*!$| zY;}L#_RA7dtcbqfoHO^X${uXrDj-DT?N(qXBYgilzuodjOSL#g+Kxzq%Xj3~<rbTk zi#A87gT(?7lslpDGNfytxjK<Nu=5Li=DHTNqNHtT1wXji>lw`6qFx2hwuB;ZW8D7A zE8ig=6@7Q*J-En!$R05UKWY29J!}Uad`bdw$e2tj)07CHJO`hr6u?>2a4Isr*}8?z zz}i~ajgdpUMNs94jp!~(zxvjA>v(y)8ciXU;G21W6Jy}R&Ar>1FJW4b$i1K=l3@-d zhqg~nx<6l8l;AoBzsn{4Rb(3^Mmy2!&27_a`rb6My_CI1>V=7cIjLh3YM4BKsRJbq zE|;(?6d#r_W3!rZ#NS(r7EiwJbp3>P*viGvg*>5%ll|m$c@xm2{zy%ULUki@iSjh; z$L=c|(1`)}fm_ffBq6_9k>DD88@nw(*=CyU>jqb+wHjnO&y`&!kObtm%`9IBm?F<6 z)<#w$kPD=US%Z(j!R2dcv&0J=)kk<WgSZIX+_v5kNR|f=*l=f}XG{E<6htt$;WU)w z-(7yyc`@u;ms+}@fU&F23TtH&)N&TxIOUMD$N}_kVfG@>5wiE7Rqw^z@CejpNVBka zDU$EE$09$b-Mm4<O}f>@_4}G)d{2mH>G?it*cJbVy)ANv=j!9(fTn&i)f5u=&8_wA z8K}Ymc6~1*fp$f2(MXP>ES+{pDS?tH(2U)jAFSwaMY>bo?ihEZs!vmK@mIu7gjjUm zYw-&Wc1FJF2k*^5Y1wcx_$R0xBP=%f(OX=hHz#<)9bENA=WvW#!SK|7+@Db}(M5Fo zlvOS+w@~SEW23;aL{!cZ9`ZO`;b5H#AM|3uwZiai-*x=KKLU8V@H0b<1ZijO$YyM{ z_t+BJRlYd&B!l&i*Hoq-fpsHr>t6?os$_8bVrn{tl@<Se#Vo_}Kx4C`crIBF3>hk` z%loX^XP9Z1NrCl^hVhPx1`T1bbkN{9wyzXHGJFu^;<a<3j9JlX0ZVP%={RR!@oW0a z@-`u9r;H5QDyK4T^i|gW{uqiTa+Y4Hty$3w3e2WV)37x#<z(uYeltNC!V^(^P+#%Z z=`PHvq3wI>ir=-N;Vi?E*7YLN$(a$GSTAV$P7_Z<m#Ky6-?7!xTW_4SPP{j=Lvo@w z5swnr$X0)e6eLu46dZCJpawI`n-~?TW|S!R6M@&NUtB{Mu?kf!wUX<&xi`ysk9c%x z_cw)lFAmz8jP^?TQR^doq?VD2Xi9}Je^n?@`!|V|oiu2<8KUJXqSxRp0=^`z*Y;A- z&0nogC^~=;O6=J)PkY0-YZLkJh(G&<sM+M|-7|oy^EBNwm)f!uAyIH5)gRw~Uk0uv zgk_K)qRQjO9pqC6s%aw9AI#b+mMeNJsWz7fArdDPv-gku@FTAnQRduzLlnS_?844O z8^h!sil^yl!0$(0FvqEn-=(YIp~uvTjs$Hur8D$;YJO#g9OENO${W5i<dqZstH%`m z7`>Cn2RKsem-fY9U~;M43F0JFS?L3YSPy#H;Ox{5(Qr;luTO>r$DtZ_wpUYY6&va4 zphkV^?ah+95)VI?-n`bVq%4X{M4if>kyTr9^v+;Ls^pcb;S?O)&2~IpS5r}Jt+L&c z-R3c`=Rj4(Y_nWugQd|*?Ot`S!F9-WSRsV0MPV0m<?xk)jK<lG|B$+=*X865cxiXO zyLw2yXZo2>Jty!-J4^pF>B&fHmh0Zc7IGxiZM?yHY3;H2Mn5+ppn2J^^$Xpb1}5M5 zDwLW^9up57vcEWu_KcEDg}(N1WrDO9hZLeTn<UQkdpyiT%&x2{SP<JIvBp|D13Pf@ za-=XV&6ICG9Ow05?L(Q2Z^2Bpt=_K1SZfykxHjq_mg4tZ&NK>l@weU*l%)_KMGfA9 z(rJ!;s4-1#Kt%u>lJM@0Tc%5MSbG>nYKp@#W-a4#OELklp>|7qb9rpTy|%oe1?kWv za1--@#9_DL3$kkwY<%%i>{RJW^w^7<w>U9B9qER-R%NLuXuG%?))MfmZP0;}sXe0~ zUPfsR*k2f+v>2Y;iExmRj#(pdeP{?aY}6%4Q#I8K{X)AlpuZJql41G?%|S~0A#~Nr zLL=z5;X2Aw+~!*hfp}nsY5RIEsz)t3fNvOYVV5E&tw{D9a|UCaLHaVavP)5bNm_NI zGr@u;@lDBGf-#421atx2=Fz5eBEKW7GL_MR8lQjKe$nCn0kO4xG*CI3iGD<0g=>dk zth;veg*ou2JzftL@mOU|7T_f<aH9LNH<vGC3O7BQGcF(q63MRRr<GzRaub{>Fb0^L z*(=1j!g-qTvMHi=uO~QWMLiVfXJ)`mY+tn310*t|r?T)gv%H@w2sP=mLJM_`E&@&R zXNl*IgVqVRMK8d0{&#*SRW1QkZgK#;zv)iY5>aTQSY?C{$gWh=cX3gihz9@ZZ7nK- zd*TwqKP^*XpHxjjcgkBn(5oo9YE6G|(-wFn3)7mDD{?Ppet&sxYU0$a{#oYWiz-Cu zg6(3A{+%W77Ir1!iD*9?B3<({)!VE3rYa8g33qm*+rJ<@$*>0`F3)(Bq_%x2Gh^@N zJxx+bxM>AyFW0SmTad?7@}Drz;bzQboYb=s2)?1ETXp#==C!4;X!+If1>}6dQB~iz zvT82?{X{1qd;Sk`?+SAG2wp6nOIubrf*pPh<(yENDo5F)vI(j}!Kxu1;`o88SkyiK z7ro^Ad6gpIsBQi$=E$;nw?o6(7uIy;rG5T*)mQ9UW%#cTsN3+EKLkC>V_1JWpX1b7 zNlk(GuZ)8jCp)t{MWnj52Wi+>$bWT_eF%p5*3Ok7sZOdPwkoiocXmj6fHckquBY{n zeM@Pi$pSAcJ|k`lbMrUib}Bm{fZa6L5n&IxlJqJ@FUkV(Xr*{DDPKj1Y%0(rz6&8g z337a+Z_akx18+Hu!*Ny@l5#}mkK!--h*wwR>N{+~Y>&Ov;t(!i-&|AL8wE5Osz|kj z;qQ+Y_~#lC^%3UO=B%}raXvLZ{0ORv89xV21jAJw-`5xXJ;|Ab!2i(#1bmcfHp;|n zs6KaIdW(TqIF=>FXwKPx&o6>bJvB_3mi%z*C*r?puyjXVWeB_WNo7}!SBuw=2T+7L z{Ix$uMQ5U>^syH8?9ZTY4g<x-f5lu2mh{slXUFw4mpZ9)q1o{RpQJ)?)p|Q`=!y}r zs=!+YZMcyLhAE<`w2U!0`mlL^;ngCnPQ`^S@Oxpq02_aJ-PM`&hH`t6k)7US1^Rlq zF=F4AzMzHw@XA(s)wtikZlyXuy4bP$A<~hYmu9}idy<M^m58fRLy*!<{QB%n`BZxA zDuW_5Sl(*hlof?KT#V6+7yTJ^T6Ih$mzr}mp<wOI^jYmaeF0ZhN91|s>g`;?pHGCQ zhRwK-6cnJ0_?=j*K>ol|3fUTDxWjelp;$X?cqc~1t6(n2a+Dl>ZGBp?@v~O@#KhoI zix5D&));be&t6bfGBN#~CU|_>G0Cu;DNSN<GjxI!s$Y^?o{Dl#cL!~P#|nhVo2v1R zc^xdeML~J?&fStF)l(*jT=&im4Y@s+Z#*YE(O>#0F}K8_t+dwONAe+oFNR9BYG<Uc z#7!eQhgPqOtoWi~k5VnD11*24czyHbP0aK$qHr=^8UU4lNbGqRq0TUtKs*p1<jCXF z5*KNn9grqE^FebE3(A2XKAUx<bDMLEz;G74II2UFi(7W-THf%<qmc~p8)S_|8UwN> zG6kF5mTOMjRAodRWDqFGyVXZ33ky&&qlt?~*LbDorPY^ZrOiR@xr)(na8A6$KZZY+ zFmu<L-q&^ZO{SuFCIbbs(WW=AH#$#rokXc5<VxV~y5#0vi{!4gh!{AQiD?pL;N3E; zd$O2+?h;vrpJ@5F*1URiEPt5cda!pf?)J@&9!5MSWkWJu@_O@n7`xvs2v=L+<7^Yb zZ_D?F!hgt-I~3TjoKT$6>q8eeZ+gc=K$!Z3dJ--EBKpr3oxNz3VS@30M#J2j7wW30 z@k1-I=1H1>6vs|J78l5~6g3`;E%K``j$>C_u;)o?sj2EQT=5zxmk4JhE3Y+{Rku5; zxnYb3<><=}%~4*Z$mMoMCy?#4i+Ql7yT&8!CdV$7+m34zEhDR?*5M#D(&{JwD<4Ns zl^Ywp^VeRH4`OdD7;ZjN1NV6y68WECSBX<qP4;)>HN#8e4m-kM&&9^~HgWp&VtoY` z@Q!Pmb~5!yZKI2?WQN(soa9!{nm3?(p*_{~>us$`D^x2zG4@Fl+}$MH=vKpnMQ{HT zcU56lmc4(0`4gpkJ$9&fveoR+Gz~3xg(IE(mT!tmv)E?1bG7t_^{AejJiSbyzzB&f z_=selJs`EdHg~~dRjXMf{X`TH1<u%QEf=fF$K|Bx0W8e*@b{^oH*DfQAzB9&hwTzz zq=Frw>mY~krVUvmoUJfrYtpJg0+;>s>p>y#(RL(?Ko7|0e0sN-XVp)69^0n4U4G}b zc~UDzVk&YH_aPFyU4udPOMsn(L8GfLyJz!m!frM4B4d4mvm!t?djlU2(e4!K6f*j; zZv0X!G<CB5rS+a=^6Bk$k~@o-#3}V`Qcdl=Y-bA%ejSxPI_yed-H9XAMSMJ%<1&cG ztcHJK9(i5QXvvokxsNe5&3J&NkDby`5DjldLha1jMeaDkdF$9u(CZ=|wx<D2(@<u` z7Jngr(JTV8tQ^AB^}ek+^pI|;4V~;9{UNMPvB`J-X^*Nd$=}In|Dy>%4L3>?qZbge z<b7X9ZuAFNz*DkN01=D`rak-oOpuIT?PAJb7WO4@6k!~a|AiycI?MBURFDgp%Ad+F z@w42@b!AsPi1r5BoeP)pmh0*GK@sClXZZ03-~c$`p<1^xXmoRUBiJMKxO+<%FKlF6 z+KM7q^RsyqXTr$+53#5tMCrT^zf4QJ`QVM<Lx1dT_FM(`b>nm~7L>WZAlYWPQUc%Q zcnha59Z$AZl*Hc$?~Rj6p^L|>G{mKW>tzzH?`x0v#$X6TZT9g&hPd%%gj=OsFs&-y z5XLTEKMkYB8|}~n6=lK9^Uwt+#;SUC!t1KVMw69VldFpB3N)#?2n(t~`>nMU^%`jD zB)_c)ZJzIsqpjC<_19W`M_6Ft-60RFksym|h9xzmGGFjf7oTz6ms`tD{j0UlD-ljc zSfRg3BVJ<1wdMA0879e6^Z96PJ$ir6&1ta^-sB%z`sD$rVrd%X?Mu2C+-b$sRnQ7Q zjEg~hndv?xF%fgB)`@5ro)hkFpq_X+K|djZ&Zpj7=q=$Zo|r*SM!bseUjD3Xr0DSL z3{*f5Z@-FtHr+14w)&8M;MqcI0KdKec5o6{qX0L8uiaeqHx-)ue}fU;!z3UM?ga7f z_(NC~0cStH6e0v?J?<p8g73hH=2Uve>%iT=qz#6LnA%3)u)m%F{fuvGZzkJf%qqt% zw+YElC+l1-tRJ+`@Knzg`pFe>G!XAxWp<6m{L52wAylj*m}TUmgcIR;`S}Zy4UnF> zAF_`~Cf2rcmG$U{R!nfZl}4yidmz)4z9bS5`k}~S+6WpoX*8V|AoE9~stJ&KrSI)1 zghsk!%nI4#5)>9{5Ht$;U`0M-b4t=gAh9|Uz%sV;xm;OnCfFrx5;@;;9rEu~_NhuL zR~$>u3#cg*<4^{8&GU_u@iA&$^Mm_kx@v{KwUwP!Na9k8yOSP{`1SHmvw)O)^x^MA zT0zLQ)CzX4q?;=J)QG=71Z$sonQaJo&lO<0Dz-S-bAV*h7O+-p`7oA^ev-U=wrmy% zYHH(R9wTniDG+m}TUMbc!3}SUc@8s19-1QZ63dD0%1oy9ol7-Ae!8~IG6B44$FA_w z<ofW&-0r2Qo9`=Vi82g-7$Rt0r4v-xm?Zm#6%N>hgEkpt6!F0Ih9!^R*<z%yJZM&& zZkCT_9H~IBteb6{0r)Su#i*MwHzk??Q^MBi)h*zIgatQ3<W;@V13Sk>q$`L+Y0btn ziNG-8D1xd@SSz{c?ZcMaB+=yiNoA7~egz6(gZUEf5+68aZ*hDN$CYX_S1CX4bUoN~ zPI_B1I&7(<VJ7^m%Zu`G8sxi}CdFmo&+<+BJ5gT)w<QbzIW&9yxxBfIu-`~|Kp8er zac)@g5l#PK*|t0bTU*m$Q@hVm63mUtmDp7ntmG`XS%>43;^lw1L?-NAU2EroE_`!1 zmgfuL#j)qgepIO7t>mo=307ChpH^Ox+~1u-DMYNkKb2QL0*(TyuSC<bv>U0#8mB`^ zQ}H2gyHpqwGrR9QLuujr1{PQNZ;o5tA6<G8D>I&dpF1C$&(<*QeiJ(hKuBtlx1i6g zD)V<9E`%;QLL^_Oe^-;mtc=z7JE~-IItTbDKleF?Q@#l&iLn|uNxETVZl3xwa40)p zIb#0~F8wKS4|_6vwWg=E)sB}dvs?#b-t|b`fuG3Fh{$Fi*ZikENkM%PNK^9Y_b@Ch zz$;3qsS7nH_JgqXPs{wf;J>Qr`5%H^b@tiB{t9(hvfk+472oYzo+KXdlg0<m<j$0@ zwGW_+@c0HuVX+{WekaMB4l4;Lk)KNfvE^(29$10-MYlL6Q%^qw-|Q<KgNN*r#VX6h z)Si?qFIdbYM~z~taegp8SCvBp5teY7>$t}@&z9P@TrkHQn6gkz8n%T5vwpr3tLHuB z__PX`87JQC<Fo4BAL$?<638&0V#f|2Tp>MvORjrKJ{I172`s;&#up`j;y!-*&e?Js z)KXklUB<ZUcu=+*6~O!R#gCAOp@$DZS(92*mE%X#ijr%rs|BhG*Tr+&mA4_-#GD1B zQIg3X!87Q-$S)#0lK5@$)oAX=6n%{UU3+<CA+&^p*VQF^-ca}Z1OfFJEZ)6OY9@2} zBfgn2oSGs)OnE&EtE>(EiJ2&!hyb5Y=$^PZfzIyMqlZ_-8n+kiz~zcXnBOJX#Vzqd zzcaDc6bcgA)8mwd%^J6^5I9^^57QPH)K7d@@MhlAo@m|Vk$VNXxm+TTZu$h0LBg|U zsv?YTa+Cofo%h3e8VgC93_Ww1$$lc#m*s`<x6TL3SP!s>Yk?}5%h)&jd45R^6wahu zdgtVieqPDiJQU^(L5^|aN-KaSZOENB74mbn88gSrhi*6odOP7BIGy#d=qxaxK<lNL zF_GQ4WjI-86K!z%c*rPFMIni4so>`&^tLrct(qg==%wd7FxH`HYjJx9%yg|tCW17) zEfGau-{}BQ1bWWb_+_v8bC=C{&%Bt4riIa!D;`^)iX2m{!AL2Kg^j>zXbs-ZzYc*G z<q2P~y@}Hrgbm%i&Bq-W<j>UhCD&r5o;tvC$1y$(xg>9o8>%*(aa%H)ZqswGOwURf zc#@`rbMXdmN(+$Q^(U2gm!a<caD6`y$ak}{O?SW;%qz9N`N-|#Z+mrIUAy|&P=$H3 z66Q^KfLjP5d%#AJ+>5W_DKKdFtL+1f>f|#_$ZTS8fYCVblP4_pn8|Ek9pXBw$OYKV zvpXWoJlQoh3>vZrho>f4`->S*A&QE~p(eVfcdzlmiRm+fqex4vcRWkC#sjqn4t8b` z{KKVZ9!9>Dd+Xt}P<6l;e46RUdNGgwiN6_K-<Z`OF3i_y&PoP+)9nBDqxBPOFG^<6 zsl?Lg5)u#tiMc+7VCKBvi;73(&3l3S<SlMVYSC!kiqt@SpLL(q=29YC)+<WwIAMZ6 zA+(p{inHj{=)anXF~Pn^p7hxAXz{_)W0)MLLAOEtK4bOMj9Davp^X}H2ukVlTt>c` zb2M8D4=((4R<};ZysyRk*LwRzyWG1lVLA-_{QbRuyS93KaGkY4YhCf@F|c7rQVg2N z98Tvxk<eGCB`LgTE_&`Y24CLP@Zh@P%H$%I?*7&;fm(|$Es&~8aknCVW?^2@>Lf9` zi_*M&kUVP!;2pt`fbHrtXR4Pu*WW-J9bn{!;=m*Yj-)jDtWAY)F7mQ{eO_FbflQQ- z*9G2h0nWBNZ->S3pL-IDiZOF0&(HiJQ+of*y<(;zz%pszZE#Y-FFm3Y7I7pis}C!Y zewgRW{dNfw#Gh@Aj0hoEY7rCRxyGn}`x$QL`0@&5#<gX%Y2B|9IJEr&CZv%`fj>g< z!slD(%Shq}uKWj|FyhHKGEa8!sNNo}IKoha(17gBh?fR7RHQHcDY51^otQEF-H$Z% zi_@n-1P3lMG#+S8sa{SO-Y5@-53+E#fMU0GZ9@ud1;X6S^PZEWq|M9+O=lkFePh99 zFi%y=((g|$Y#M2=ni`~DPmyVEG*={#W!v3|ER{)CCl$FDdZXs7;aHqb-bm|7>)QY; zf~P0YxcGU*D<Y7~kw=A$rfHi%*_UA!pqkiN#YgYOc!O?ed|1RE(rnIYKoVcsfdOk< zfCYjFUK7v~35h<c#+zTXt*pL6hc@Lqei9JpUeo$ulZO*-9vx5DUrB3X6rkRQQovXK zY^N*R*4f?JzahxmJho}F@sZ2AThgJ8@)YZ8;L;u^VWp6g;xFk31HX6PznG@pmU-2F zKw%cu@~opJadz72+2lvB))`ihXq#AkY3jRmhUlM<t(MS78+0D5{>i55pgJuel^^8q z)#~){H*2}iJAHpMSS83q;H34-`lHQ7cY~TxNtd*<#$Ts%0LBB?Gnba6V~|&G0!kCV zWSr-&N@4Ai<95}D1#Qh%na-D#sz$w1wrczF>O#Wml*fbF#_ADOXJBV2bXAPFMza9V zD9Q+3R9X@qKdOtW)y&ts4{$VYur1SuewI58!@@tgO)Y?&>|MOu>)bqzZ%}ROa{W7t zS~{W6VJ{8{^3W<UY!RPY(t$XYW~tiP${2PdXmR7w#2YNvjE{`Ze{N#2Otx#R@HSa< zRGm9`)pdJeaFncSw(w5*WPboho)g^5BwP+0(79RL$`>IslqFIp%KMu`-^+yRPdlYI zi@iEWU@?-s4KCV|ijbBFjOXz1SiT5<Xup%a@Hcb$)qx$F5sE^04EC9wHYJ!6-Sye~ zG{)aL42z9j)gJXo8s%e7(wb>e^4L|IDs+=&Vs6Y;QWyivDJpR$ECbGEi78oTk%+Yt zPE<KCg&l2A7CG<-qFLZJbKR>qVdee3ROA8?a(Kpg9GV6r!knQ$)?Rh*6+-#6^b3x< zlK!nlTgwDRv&#yhO1kKM4R|OcI=1zo`vN{cju)--xb=&yvaiU~>(wzvC%0MRgGs1H zF=con&k=n>^~{Fzkk0P!g-2&leHT+h?7kV(p2Q!AfD$TLuh^2pVyF__H4HG6%vrT6 zqG}y0Q&xYP4K4qo734Y^o0B$s4n3B}w*&Uey9-Dqg>n|Pj)VB$yG;}!<p<UVzWFJS zW|4TG%Z(L(u`gr6XAQ^Ld0Wt#P`Hn#O3hrh|H>q?MpgvvJ$E8|44A`oD4n3sztzOL zU2_b5uXj(eeY)bntV{%nQK_{$V(}@_nE&MH!K|YA>NpW15x|e4j~^YMmEQu{a?F+# z``u7z1ol5i=-F^~{<-?(Bx4A0%OlD6#oN~((74*&Sa>etwtRlUJedGgAh8D70;~DE z-t;;~P@xeUy>()3eWhn;f9D>N1$ESJRgVU(w{=CyZ?vB$c8_zDOsU&u2vbdA&$s7) zKQPccrRvJ-W{-wR>d{;EC1stT#Y`p`1R@cgzj(j&UC;M7_mrK1hs=^D^k}Tc?jxvj z(tsBtxVXL>W%i7WUa^UBLBWW|U72FWC9nP7hc6IZpY5e3Py1fGb=5tKCnt0nUpXT1 zu>5bBxj-1}C%S7oZ0Hxc_eP#Lywd{i5p_5WaSC|x@7w0qE4N(~Y0Z>+0&#G4&ec~Q zNhgc*T=KBM;v(MDq06dPDn|#^i>7_kAQ<{`$ioJZ4{Jcu@}T7QEmeW<l+6GEltP$4 zm|MYD_wHpprA0FL3&C!2JaK37!%z4&vXbAlf5x*y_owLg!F`;m+INNLTS4<61Z5PC zgr4gVKp8z<Bfo5nxylrV<^qa>+axLFnoG;G2r|+bQ{)DRw26#+kOaa_qscsWm?&?A zp8eC&EuyDKy|Jcv-5MJ3ZugG)9M})$Fcjj~p;bV)=D(VIxck7;`1}18V-9a_U6g@0 zS11-h=E-<i!c9OY<rapy--U^zr9>S<w_{x71hl0s#jin*6vakGDijr3t?pmYc#Pb& z&~;M3kJe&*e|eFP|4jL)a<xWHMfRhtyiyc=7#Jt%)q1@1(XcCE(jXPo@Vvc{_JDdx zU36?WXTe}%DQ@K$mJ}B|mSS&o<FeKM(w<fZ@Z>I|SaK`cp_b5x2u0IqbBBxG@=hW! z5fnAY-B*qkY_2Me&D~=kNDHf2gN({APY*M&DYGQl*Nm1l@~3D_Y8NuJuYFM!vYeH? z>g?>#XkM)h!4#1d(WE;vLSjO+vl<k2)Mm-QQoMjM8lex!y9K3S=sd!9+6^4CYOwI! zg*CP+@MlqHbM_^|24d;Nq?f~ssH7XiqR^uVRYZR&r;;bzc4l#e$3@L{Rv*J09Gg%z z#$Q(LgJ)gM2gYs3tSviF$n8VL6;4pZMp1<C%>WFtXPWs^iygPmN}aXY`+|qKPRYuA z;<#?uzZFr>YY933M)x=Rd+Yh(v)U<rcF9OB!l|4c0a`?Kg3_=@XzUgb;&&y;QA*zp z@4ceqMUfA#ddsD~mKOR9c2!)9-*>Dga1Zd1-M#Zr6uJ!`Av{EHkN=d<@1%#;z^AYi zL_=rR(mq#7ic@?jo?V0^1e&l?Tiu9ADDSh{@?CZA@`|aX@oYZPT)uFLd2Pea7k+tx zPj23XU2o+wxP62=Eq|pZO)ZZq&l-!>CO$tqr~~NUiLfK_2Mk2)!}q1A#XGifYG}yz zUk%a9$l>OwGE+M)+j1av`lMd-J3<%o7s~Vj-JiORy3Ig(%>FF?sE%u8OomJy*dr(l zmjWQE3siNfm-da7zMCVGs`%Ej)e-)$zre@hj!lMm9eJaWVKQaqjc7srwyeCGfkDAQ z8Q{k@?&XF#?~F`X2{&bRvi0OG>}U$l`KuZ2@TwtWpl==G9sF4iI^)xGh@$ZQb2e&V zR$^rON6I=%SFhe&p0Tg@dV=P?Tk-u=7&QyHD7eR}i!h%Fd+dEfK1Dag#TPz!e3Qms zWby`qI*oGD`0rOI`^G4E{?41npRD?fRl&a{@pJXyt%v@k-Z`%Ds)a_dkMh)?f;{l& z-NMlPh+8jQdc;CQMgtevYxQrJ1?}v|id0*xfHaPJ6c_%0{(9>i7K*93d9S0F5)uu$ z7vl-LFWbLVN$ZA_ftj~f_rMg+0-MJ}e1ZM;`wsIM`=Fy=?)Nv*sXlt*Z=6uWEw8nY zB#`r&^LdoTXsvul?$REi%Npgy-y~mq$c3yUyI^1Rnk<Y5tO?3FDRRpDX4OUB>AR2< z49k3${QuGKp8si(TspnH3&Nuk9=n;Dp1cdVpxa4GXA4Fs!AB#FV)TUtc%3MTnlwD2 z6-?H()#gW!Cz&b}`b|ALT*oyhrf&m&;tw+^rnS$1d{sV#QiyK2hw2{wBlu{s-L~eQ z#jQ=xywrl~{wB%pk4}otGU@_`bd)KDoXxrB9`CR8@3P6YK_tDJWx+=IDpNGTUX5@d z9$)+Sm$p<Wz-lZ;Qfh!rah2{o>Iap^VE3iAXTnR@E%~!#dM>3nU_zG)+H_5HcUyk( zSmLTSj~@l03==j0?jVg&rBg^7DJ_RKnKLhImXJiF23MNan!Y5OBl*bQ0GtmW+P+|V zv#qkTc5+?*;;Iny1Nw&Y1}fO_Sa_itO((n5sw5NS{(j^#t;pn$9;?p%8Q7tINaE?8 z>E!#9+*a(c&ah8mTwD%(njDXSsWLr(x5bmEW)7(`I`n`8kFsF{SP<-Od3^>|yX%tc zG!bAA!c%4$0y<-zL~-F6L*r4RIFn1;ISb`QIkY5`b}(xnoP5qW`Bp4B2wFE%ObFzZ z%avmSZ?GGPhLA1tgpHRtT2wLrmGF9cPJnA0E4>=mopc;&slL^MsB#vb+5Y{-1z#bw z{HEw5QPM(&r-4yoSoqRfJx3*XL*)~wy_2svemo^-8ht795L;KGEe2G(Pwj{8I$e#J zVfFXxIF!%~HoH2Rfn)4DmNz%Jvy81wjgAk8PA67d`Ki)5*m>{$h**FBKurJPhw2yU z(^xE$U2BIYGq@1#-C%I;yy`5pLeG>2Y_z5^$wJ93(YGJFgv;OMRMcXFwn^^1uxUtQ zO7uS}(2)GBe+f)4vLh_8pE!0ARezE=+hfl5AZ3;)hMu9T`OI=CM6@YaDFQLYRGJ2o z*Hgda{a5EXb?bHKbp614=0jCv9(B5V+r71TQujxfxUcX%@WtNzJrMXs&0*_Lzt@wj zZ`^USQtH^?Eff!FJ;b))t2lA=Um?SMbs{d9ddILR>;qp~aLFpr8waS1#z;qD{QkGD z@>$^_N+lK=GES-b;wD?P-*DK_5Wub1YE_kZBjFr9aX{sy(JX{h=Ft@Ef3c=$=Kr|$ zDORghmSR}&NK=j`{nJr9gB;VwI!eHd<WOsKH44)ULW_TAE1yqsDlo?L^;H^jf2>>z z;M1sw<iuc~76SN{N63|ZiDol--twsP3GQxAGwwBe4OG;<ynNh~IotiOtHbEv$!Cfa zC;(b7wke}-j!7Z(d&~vJ2kt(+axwu_3&Q<Z@P?6O#RGqSno1-AgvY%5x<UV6n&8ut z{}PUVvRnlbszM8^fXsLPwsLS;zwh7vOD|0j^2EcvazTO)Vr!F&#rXQ4d-VKIeBxMo zBvGMRrbI|D{^#L7A7nUjqm7uN|D^B!-;(2=|4)70g%|g~#@bULilr4&eM{Z{pEu?I z4(7ql9m<Q$d)O6{{QsQ2X|fWZNgQad<U@;mUa2?r8><htIe4xWbIqL^4G?=~2r$FG zhhgd2_3*r!Qn2N#<{|jV`{niFe;x%b#?#6rpotUtjFWh<=tRduvP=AqZxDO+HhDq3 z+>{o-`c;lU6}am~eyb|z*@7|nKE)#UN|xCm?bXk5;9E9DZ#_mN;I9fv5PD(CA=bic z`)wibV1{IZ5C7|i|Jh;=2Usg(zlfPP)*JjK9!UDZJhRo6#8vrbqng%)K91}`Kx^~8 zC`eLpZQfuk$}YPq7l3~vIrx>^;KmvWeZ|9=WFFHwOfmBz`J>YRD-Vton1P~AxAlB9 z&XK_W9<C;{2iQkDl06FO3-=fH%HdVo$86A9vRqQDAksrAebFZK`780+e;!zdinmgg z6mlsBG3Hml7L?cZu|qO3+0&>D6gjz7nuM8Oa=+1n%4Lmz5`t%t1GI)b*Xf}k)E5|0 z=u{s>P(i;`bS{caSJU@3+Xn%8Gcxo_<1y9>&TyLRV-fN1wogyDc9sIEOPADg*$e^k zWBM3KQYUh;uY&@xSZ6tc0)U`;wYQ{Q-+?(d%emnkS%lv5v^h8IbKI>GZP=~lTiy}c z^>?CAbFx2^T}GO$+^H`Fg?5zo{p#$S&pQ*W+W&Ob&v{Y)D6faPwqhWrT0Z%ThtrQ_ zx6490y*NthnOCBpyrzvyT1w{G#!lZOI1jYLEey4q_FoFf{i6kFamLkIsWy#B^!dkC zCH|u~H(4kUd=5-OJy&$@t;ffD*IxZT8UYFpd?DJ=mbs(>0vi5#*JFl{%Gm9rdGzef zP#A*ux054EQ5SFI>%{m>!YbmcRf*M$F2TurDi0Kkit9A9Qzz>fOu`S$JNm~5W4ya= zrM<V*CqXCjEAPbPXv@uvKt_UfzDHV}U?91@9ap}ehv34F!NtXb$>jJjcI^=I60>2R z<BJfl(9*XOR817OO6SUG(9*Q4CtvZ}CMJk>U<IS8$e;vTgRCo`>k2YhX5t@$$tSHn z%q^G8fbSoLhy3OuBlDI6rB{ddkjgbHf;Y%W$RAuq42`Rkk6(OUp|EngeyJf}BG~Ym zY|;D7ed_4`6wx9QjAev;M0oVv7Oq$IHi<}-qK9ApMx+(hRgn3foyjrr*kUPr?E2e# zmjkv{_3j<?6Zwj>{q0tJ8vu(PCm9DNvSG%&u~pEv!eW;tX;%O59w*-KAycH2iMf1X z*hZw5Mm`%xS&5m2a^ZMXyQ%vfZg{72s~5jj#JHO6$t~`&H>Zc~l5bFTM)YiT(1NZR zj4O8^kM{|3Gbb>W;y&^z3AEh=oqWkuR;|k3RsDkeD{gpvTf4n}DPgk#1M9mc3u?4x z9%J5xYC|?})#W>`X)Z&4cI46K40N$*=?uDZ&E;$pM0;2%uIh7v>X{kI)j9SezD$8M zmr%!PZhx)^NxQzsO;w<nZJM*)oL`xL22D}(U}wf)%+}7W`kCoGpOWOK<&7jd^D8X* zcY9-khyDp+%#*|8{R6cL2fqiu3X%hV=INZ1peZXW>#ps=gDc6Y!>;;aCF-JV9lmpP zkk87)>;vpMTC%f*&{T!#5wbr9J}m5hc11h=c3HoDA>>H}F;AHRJ$?(q2J%6>v?c~0 z1jfqjZ1ma@wh6ui$`&Y|jNS&<x2QhRx7242NWf<7aH2&di{IyH3~is$oDuoMT4CHF zekO}{Ddx7o$K%J^hmoMt>grf_BrHnc68183ufY!9j!fG(!u&<ff2M-y;$=L6qFr0J z&9{vt8^VMX902TXZuSI5T7GqF1^R4<_%#w@Ps*y`-rg1DDy)&Fb!H$#{C!)$M*lFz z_4F4Ef3U4)@$Oggs{*kpl>HCjgnTh{By2o})7skJr=lk%38werufDLaOAET39?rQG zuMG)U;bkl#m+{Z@9#PT8V7i->*PNsx%-Sj&twf%Cpet*8iK9oLqNRu0*DGvQ`zi<T zHp%%LBZD0kc6iboK}uRm^}nJ)A2l}O@u6>uyA~eM>`PmZc55ubS$o|*N#sGo8b`nR zL%Sws()IE{=@EY*2Te#}kztXJkhZn$uO3ZU&k(Qj3~s6#oH_C;2hrQo%Y!xT7h&2I z-qv3Q7VQ(~-#Flhz_2dxM1WyiVcm{{&{g-foDL_`rJO5j3aqv3yalIR3FPJ!QN8kB zELH5OP+=#XpMUMo&&5`|3SG1U3PNMk>3cvD)hAivOs2X!#Dv--fzMr|(WF+Lr)^mj zVY91xLsz3tK7db0uf^!|g4#_hC@4+vxMcN(I}(4D=GXcGSgtr{8a2=bmARSx?|feE zlR3Vsk*wVv2^QuH_(f`#^1&F%Emv5X3VnMx@gfmN@b9hl?szG9_fEPqIt`^3xz^C} zL>e~QKqm4r_?O*yT()eW9<)qdJIJBv=x8Djd^(kt8!ojrmfPtY^%j*~5_^0Alpgp8 z-L^XTy<&04OmN#SZ_1KD@Kr$R7(eB4TFZq_&$~7`=`tjdxwcv;Z`KQ3=ze_>#pe%F z;a*2P3)Lv)5gFNH+JSbd*EL%B95zXxIa@Igc=(U!;(@s`xo5eYeTaYTRk}JqT5zcg zp?)WwF*al?E<xAqjWPEof4OYq*W{&LaQ``>S#us`!z;>)4*KosdF3y#D(4aAC$HjS zmx>6W8S1CWtiu5oV=oX#JsT{5AK*ur%|z$Q)^@5A+N8zUtCo(oPgAk0DJ35##UJ;s zbFU7wk22fubt!0;o^hrFRJ-nAR_iZ%gr#RHwc1)Ng9&#rU@<=*AIkUmku4e)<cAU| zmp&7A6(wiiD-yn_77(G4%x6iP!%2?jgL=KAO{qZy6@n&z{aG|VM%Nou^QGzg`R&$A zS5%y=^DEYN+(oct=5&G58UAp>uEQ#9O*;pT0oA$W<%|KTNvVVEG!OH)&6^@bGqA-9 zKQoMy#)_7k9wCzfZe@PF07muHG>0_Q0sAZZtB+&A-6mJGG3^<dy@B9~m@xb(R1Ff= zl|tjdWA&vK*GZ5Uh^kclLb4fm>GzTp-D~F0pFNQdFBdhfRMd4eJ%*2~Sxm&No6VCk zkBVy`HHNP}-|~ssG+Yku$j=qntImLmKVa^itw>LI6``B`ULE_{xkB|VJomZeYjh_d zx02*{I&v}Aq`7!kXN^%t(j-y9U6mF?FxNr#dA_@eS{()NhuE%10a2oX{`K~G0{EhS z>c>>zSjUUZIUjC~2E*~nb&ON^1n0;Jg`8`Rf~_Vo);*~f=V=%*OL0%>Bhk-1#&ppN zjMtw#cf#BN)e$KLjEhdS(d|~>JcM?CP5G(84^#_?y@{>^ES$<Qbmow+pB$bBa*zld zeq`u&W1Y>^Ii)rO6!5*r43A0wdAX?1{b-#%L+=B^USvOS@0Wy1%^&0F_NNOj`$nPT zefR7KM_~lMV2v^7uOf?{;Em5*-DDDcBY}fJ?Sx|DHHD3XrE)72$nBcMofJ@7Q95kF z!2~^Z7D<M#n>fP%s5G&gNFArly)3G%WtBX)xu6+uw!##iydm}*u64tqfl+WOqv79I zl5u4-!_X%;;%f1{p9#i!qK~LFgSR>`PK9@>d+e}6PVkLiZet3SI`?etn;PYwWEiaC zg@@C;@+v_ph?s63K&>-$g}C2am<X%?!fM|LHVdNB;ZqcWGT7y>O=V3|DDQNJ4kop< z@?wqT=TWL8V|kqOWc2BvE(@0fmk%Tb)Q)m%o^D*G$tQl0fVT^IsI5pO&o>EsRnkI; zqOs>lc}$$Hd<lQ{1m3(knKu$<HZE<-en>N}cjVZu*%uG&%Z^q5Ew#4YTX^+s;pofr zbA}2dkj<ftx=t8rMtjNA#Z36{_8uOR-m)sXe(4e_okN~XJdUlFzc{2&3Y6!;yQO4B z*M4#??Y#1?s2;8nTG$Ds9~)nIftjGJab8r1_nT3c`da~@C+TD4$6=H3HMp%vczgGY zq;(E~AbR>}H$)bvf#=D3V1!d(jjBGBy?N!Bd8O|<wzmBs1ZFV$2h8|zc(8L0!Zs&A z0e$eXRGQg{;Sm%O%jbWe@*4ZK1i;Y3@aqWU&HuQ=;tM1=`wW33yYDOQbT9!5H$;w{ zWa!$hE#42_AG!Q&T?JC0!`bYR>ME%M**<(aHo9_;454ylO0kqh2_Z^@j51Hy3D<g_ zd7M|s9Rrj`dKbTa5Tz1DAK-nt>u(wE9G^|O3)!DP*bh7|Ixca<UwyXNl~^)(wi+&- z41|3gH8e5?Sr^CLNa;z!R!4f2VOfw&4k}?;$;!Sw_0%DB2pfb4;&OVt(cA~uKYc7# z(LSLcyQrroZlnsy{~Nv~b|-%6cx`Pz`i!-I`?$5;D7lpDs3j1rri`g6bpk8L9SgJW z%&PGIr1iyPmA_UVy;QE!)^Bxe0h_W~lB$$feLaxw$;vzqo!S%Zfm?wtt30D>tNP5< zQT1cYCnyaf0Sfg$d)yhJIpAf!!+%zuQTCCgC;z;|S&cw%A-HvKV|fa0^~rF(aushl zSk-Kw95rJjisE?7QWa1cRGD`Xi1!Qt5gIs0dgCFo=Jl6}D4NYtt%<D3-HSEjo2oSn zx#}$`_}VSfS-G_}(=B<}5gWy&Y>R%7=+yX7V(b}~fRDYqpNT#KimHzqrG@xi=cTDa zLtYL+r^h~5qs36fiin@^S|?Q7PSqJZnwYL$2Bbh!^HXzEkuXPlS{EMlcb97wdq-Z3 zU0saOc=%u<FXL9+assFG9JNP+=4DEMhYkGq4m66~Q$bkud4vExEk@OEhO#u}0$b2% zuS=ULPx5qp>_loAAQ9~3u`*&pq218n%0ub1W(_mzH*s+A9i71m2wKS&s-<ZZdnX34 zZ+1Yp*D6RMjia7?_0v~nO7xC3o})Iis&j=j^`>s)-2xGo*z*3hcWp177#l+8UP!QJ zFTA?<$-8{){$|i)*%eKMLaIOVXT{I$7%_T?i@UE5kAorXs<Zv-YjIvNX2Il?&J|W& zc%zfRROs)ib!-NdEE`oq-|RmGR~DP!`m{}ayTW?;8QQ0VVfRMO$nBcJ5LA!AUR)Gc zMBK4v%2gYZ@Rv2z78ss2dVuV0fCr$rS!OGOx$B|r+9i4LHQ4t+!Sy3*r&$<g7(bL( zn|Dj3jmpEPjjhWpqZdcjmenl&UEAL$Li;dh>3Yn-W@L0U4ZxPrvKfZ;piVm)#u@%M z950%_UM0cJyv>;*3Kyr9OkI?b`U$UzLeSYHgFm|vY73QbG29;DTUA!%h%bn@C`LJ{ z-!sy4K;WB=mB1u9FIy|JERNJ|oZw@Lhnl(5EPFSFvLrPv?SYX-WhkAt(SG}@F%|A` zYHdyKkb>@oa*lk#Cb@iTU}*Gimt+sJpBV<t3xuO&L(K!{CKrM6Vgq;<Y^a;4J}1U_ z@oWmIt>x|49GhDG&9N&)ly+1|BT_n4wc9hogp0{(Ynict5N0~mV7OwKdFMwko`<sA zFbe!L<U7J<ldAZCb{1P!Eu1bCa&kt@O>Jvy6ZtB~YU+S&e=)y<+euZytO|#53UJZ# zUbht<s6og@XB(6D<XE4=pOc}V0mKlzx_Er?houuLb$})DJ~AXqhDzKHhBqL<Q7d$b ztT9rs`RNboC6|ZY$}|OQY3VO5Q$h2^KAcf|EImkRY2xkVITdkwX<8>8AJ<X;lnuc< zkq(j96#JuPwPnZD7?-%0c#HV#afI$QI>6x8S2<#j;;1m35lma8fzQIMD*y`JoDzUK zn|jSV%%F(iW&ASS;nW@r{$kXk)bD!tCp&AlyS9h6xOX(0CqpafAXIj|eZ1&5S~Q<Z z88r;_)(kX+tgBkawQ+ZX{u(b8l|9)A4d7FLmY7O0I8ZAkg&v!^$xeGA6{Q!<i}g^v z-0ogTZN=}NSy$*5Tf1($thnT8UF4AEk@eqsUw4Q2*f~r}$1U{PBm}t5$?yMq1TUdS zyGnZ($FR{|XyOjCt5e-sc&%&F=eA&ai>4N*Hkd1p7pwjng??1>;`<k4ek#CoHPv1< z3&nMIvPHSkddwJ>e=xbKcVt&~u(Y<FS{OOGA8@5O2r357Th`oFU2PClvNV7a7{{zF zf*r2HrK$yr_KS`I7BSp%UJrhaPWO3!F?!f<)!!Bmr)F6w492pz1fJe>^oA}4C}6YB z6ng^hMW`W-PH(D?5_MXxOjTP{CDd0EQvb1f{u-W2jnc~%v>u8Jl>4dbr%7I0r4Kff z+fS^Ls%w7cVEhmT=ZHLe3*mIxH1oEus45eHP?5ZTy+fhfy=9v5a~_@$pC;)Tacs0b z!~@o1)Vd-dr*e(^zC|iQdWLq_B|;>#qh2GyTNMl=69D(CSzniEbc0>D@uP`J6YoyJ zIYHtycsk1n8F;JVwZUirzs|GhD`e>#MBo@3kQulf?3*HW!85GgZ6O&^b0#zFU~*ua zszr|3d+-L%<PrWO#p5%#a^T%}uNtp6?<F+5)hzg`#KyO$yRug2SMNuv@f~p*#;>+@ zDx&{D<}%S?blO=5RA2@>5ySq~#jU3OCg6M=hf^s(n9P&7PM)=JHEsJo)#Z(#KR6xC z@tj&nsU(Z~6>a#qhWiyGV)xY<xvTC%?x&qEP(J7G#(62dfzt1_J;7$*5Fa6^)>{T^ zWo3XND6%zuRjW1O>UGBD?@PZiM(EYijB$WzrfIppCYOZOo`WvFeviXZ<B=v8L|44( z5GCsI!E1urDu$oz+I7i8te>>W36gnUi1x8oz-C48)jH*{(#K4cgLlPASNei=2V;qy z8+66Y#ZEJ<vvCa`y)7)O&65jTON8sT{0DQcBn3Oi%?y*e4ZnFQuM6QxZ}wL8Hm^2d z66i-ZM*-W|jSBAmbqB&#Ei(-{S7u{3mCL2xn!wc5Bk5FXM`5+N31_<V;IH&+PDuV1 zDJ1B??Vu!V<<6O~1@4+~$;K^*R(nF!Xs;XHDLDFAicMVATFfWTi_)Q&f?PMJ6P=~) zZj;l!7J#=p6$3~AEJ0nJ^3mx!{DE2>HYs%K-2VL=hgxO9DWx@P>Y$I?URyli4}z&8 z8<O8tm0836y=+Qav3t;sPb(<GU!k<0W!R-10JzljkM_~pq-GwjE&mT=Zy6R<+w~9A zUD6#QGIU8d3Mk#3f^>J+5Q+*a-5}B(L&pFT(mhBH9YZ$^@s9WPT-P06{_ioLW*;;A zJonmbt#kFyaC?6AH;To}yFP`C&dB=}7LZsIVMG!77|tpBki2p%3krYZyY|FQB$%9X zgEPI|?l2@9`Hb$&?@TInYGbN7M;LRVv0=8s3T*KW*-6c~XwuixyBX9A>kD%=U1{k6 zf<;C}CYy*(14-+DnIn@koM9<rG!Z!TgT%kM{yNe+e!@A|xU<yj4fZWMQdl{Zp;diB zb~?JL&)I}}NP`E&V~7CNjl9K0f^g~kLLkRAHwiR3WWWo1M@iUBASRrx>Czq!LajbL zxG)tyL}4-WS1iFMFz**rlpoNmWA?lRw;kX3l9yvh$CyVR#XwV!QrAqdqCVoV)koxY zF!j2Ta$>+<!BU7`<(f0*j!co;<E}3L^jyhWLCr8*wqBS-ZW{|QKR!gg!A93V8w2{w zy!md3YIFRs^drQyB-FlWQs2@!<Kl*f78*F%-($;4DJ_uP(us5P=0=+IS=gp>nS(Qm zk^I5S1FPy?CGP^+>W0mhI+$a`7<bOAoOP%s)RIpyIwA_)m0~nz&6_1BaU<MES9xZ1 zrRwVEgm(Yfe%MfOC0=_nic~y2626mLEo%wv)~Ob1IINCC0>)c@Ty^Kw7YKE3Mwurq ztivCTSMtB?7I0e%u!4`gfnGH~ri6^-o{Jl=Ps#v_|0MbN=R6P8=X20Gt?xs%9sjMp zzVPqwX!A&(6&IR*Xnor>iJbYS(y?;7mSzAPT;NK2)AgQRbTfWCJY7Z+0(V;<^3dK3 zLS9i+6e`xkuZJY14L*eCMXa?tx{2n@kXB&taBs3~b4Rgan%mb0;_d9&iPH))!mNPm z8s?Ojuffws>>K(TQW{S;BktSLDA3zUNO9GJU+fRDY9l(RRLg`IA_acgQfF3h-~2PE z|J?`rB;qwBd<Mgw{S@8Q#FM+-gyW65;R-f?8A3D0EptRtDHH6T?8qb&-T>Lfb@-CJ zvHwi;Q4{ArHXE>qDSp?H(>{gMC^6~bs*3atASX8bcNFYBzx<d_TN-w;fuAwTjeKK0 z5`<P`0%I;Dhnkc4l%apCUbk8`GCw~w)jtS_H@(%d@E<Vf@52+Qyl*0|HErZ)^3<a} z$;YxaQDyJT8jLbdS6mgB=}mJ3L#y@W#;mSF*kD8d{7>%g@Vu!m#T2RjxhBf*i;|E} zY-E@5=gm5NXnvWuo#z-oR}!{9PzdCDJ;P5F1#|1*d4&AET`Z;$*8t9XdE}8#xacBg z4XEyFEML&v<dolWY20E8{y0O;nbiCncTEoK#61677l1R2ggG7UZ)jVDJ{c5}%nOR6 z*Qj|D^B)Kg0GN)_`vO5|H^BWL%FDg4Rr|kqjh+tnf8b*$I<t$m$py>w0IdF@B^J{w z!y>J?cu}lkgPPZje=Dv?qVG=HhG0sc2)$3(b^pB&G7gM2WPJRD|GA1dPM3|(&A-b0 z`?+8JE_}<HteTr-ktq1zD^4ann-5?6pj&ADZzK%Fw&e>VMG)Jgv-yvGlxzQv6Ba-w z`>67Z^xv1?W#NFIVgLKP92OFp?`5rW#D9Qgpd70Gboz*rPK7Ry@^|bVF;??kh7v}8 zHImvVJL3NU#Q-dvN1loX$n-w{pVirzl4rwk^MC$>et~!wVo6;pn?wLJgQIo<?f)P= zzcNdVelejP<-1S+{gMVx_%M;lILQC!b>LF=sEx<+StkDv(RyVh5Q)57ZIB8AB^$-N zMGWKoYe3NhC($d$@LXjLni8hmP~EFYZe%RhPp2Mof9nMVID4mJe7F^*>`8Yt%Cmc0 zj&Vb83a`MthxwL@y*7;KxM-T_q0YA8H>RE)zcFkZvQ;Io$m&7QL#6^G(O$s`&j@2S zMayg|9_Z6YUGKE=Ww7YUX(<xXVRAy}OI~!DZ$>%PXqvbt;vrWU@VEU`G5GZf^P&ge zGu{48ofDYO{?A(Mdg4!={nR$mgD(9`$#M~OQRW}BiBc$SU6Rr8FAyKKhjW1jr3!q| zVHme8MX7f9(*7(#m{5k{Jnj|FNF;a;e1Z&Q^s*S}{lb@9SgJVft#PQev#%kE7jpO^ zDIOAm!;v2?PnST#^>2O)p^=n)^PLWmP04%0z+hRZqyIM1jxnK3B}&^$eO_HXy1nk| zYVqh~>cDiyM8ISzuinA_aWbvCXk=L&jtO+BQ>z+6?l%Zzfy2QevsUe$Ih766`53?} zdDnahCegg;k7pb2jwN^3VRgixB$qOp<B?##4ME0M8tId%E&uOWzTXws_rrVq<!F-J zpt7JJZTOrUlrfA@Yw*)YMJ5C`UPYUxg``8Pg(EV?$JNx;m&YH^xGwz(GhM~{Tl-`! zAHN&Qe?_43>NonLc>H3~=YIVU)!$2}|D|qoR=n{AIP{#L^Xv2568OpYdg`zg>DEnu zq)iej&0Cowb~>wtNA$Z}c>At2U59wv@uvI|(rfFR`>aw4tzMI65-+;n{+2~mvgYRI zYDf5{3cgZv?wHfry0DY<9PqcXWb_J399=yf{|+sZ9p0*7n^qsF_k?HtHoKei)bi|J z7t4Xx{-&^Ha9^KfMlHCq&bOhHV2Ro{YoVD74HGQ|%@uVfH(MJ5Z2Vmo4iPk<X_2yj ziIV8V{fiSjTx~%-d!Gm$+5aK$;Rq?;9~sr!=H2smy5SeQG%3yj6#OyPn>Lah6hHov z@}CT<f1d^LtFqT6CDT7J{6cngS72nt{8b)71rYi<D5tF`YV*Pw_m*JT9k>k~!6W0V zDOqMrc-!xd>1_!nFw!7tx_?Xw4W2`U`LDrSpDMB%vw)Re^fM(S`+%ji21iyCwKk`Y zHN@aNiK>7^VBEt2E2xtI-DveNLbH1JU<BFNLAJRwsuB+$xMh`b^<jY!5$yzxn1wA> z?PJfu@pjs#sagmbj}s7c1=^Wzx<62{D#$8rB64kw-E?E(Ro1UwHcps&An@g0QV7R% z;ozDX)CZmLAE(-vI+r3<tQ$)&>@gQNgB0eOy&22k*)@iAGRFO44G&6RvQv2@zLn8x z&n`Y1{-cV%4|8-ykkunq<jtNvjQxRkOL)M=HyhO2q$viqZS7Hy<n=`<#_2!qJ>-CR z>YIn7HV9aWzv=hP1}t1Z<N!MA^V`ertpH61)wScO^+NR<0McOHbJGdF0lL&H`{ftK zOCXyh>QGc3bwp*2{TI^E;|t(TF8V3PpXlCJa=GAN(LMB69VE??r>Ka?_~33dQh_gQ zi0-Q|e<ya3bjUVIt28?7W5Z1YM{ab9z)TPLRY8fz6TDy19nmuH(}_x6RYrc4O;Iqx zi8&JEIyOHSO6)Ka>+~YDGxpE92s?npYroOGWK(8@qOZ&@kD)rImJKH!0smnQw0nOg z8r0@G93Qd~|J?QcoFDyJ?fWRXf*(4qfX0(rFueYFmMgIuT!MwTw%`{x;hhZcq!oA( zIMucd_aP%#oOqe^8YEt4Ua~C|OqP~G7H4kb$qsZNy|C<ff9%C&Z<-?^^4jv>3v~#T zje-U}Rd7S=W};9Hej`4Ht?jnF?O9-yIkJAo&^|vnTQ+e1(3#Mgc|q^BSIpMMdbaZ{ zlF|9+9Eb!Cf5)%l%-{44GxP3u@D4ZSP~*@*N{gOLtD?oRiKifPqtJo-QmDp+epr%1 zV#<F$pt<EL$2iL2WzHRpdH5d-;{jf;KQ!SnfE);)3@(^r+D=%6crcjsaBhW<k>ql+ z$lmC)4>HF~Xxts=qZEhl!S>KB&{wbJT#^=PZdAK!W}iSgBWK(lT`B~+vVKuLP+`lw zCzj)F>a>0>w1~Ftwygt^s6GNw%-JFlAt4g98>j@dB8U1_KPM8Szffd8Iy~?jJPZ!r z>6Uffr{|@?>okHbK@Pe(!0W5^7~!k1$Puo@Er#w%`k{P%GZe@hh=0nBe%w)iUeuwH zYDHw+so3AO0L&5wf1?H=?3A>vkfSs6eE|GxHlBhda%NEY<?;4gnAZF+A=K2Nr|B{( zOFI}7MU2`*vA_7cM>I<eOQyj5y|w)&PmBud?^>c*_ZhNuPQYxu)56mNCJ|_gcMBsq zk)K?sHU$WKTFPjo7tfy`<7r<fWlP)--7eo!?g+Fs_UAjwq~t2>I&_cjFSYwLA|~LZ z-56SR9XjeCyzHu3shO`K6sa_|!r=X;sgejY{znAY;=dCQZtozI!Hy;@3r8k8#ye29 z`L?OH{oP#L54>6Kqv<>c@+dRcO<l!0k!|1EdfPf%2nYVgtmGRNHqi$A5AJT`pp!cq zgXq4TSziukA~ntL&o9GVyO`HY<EzNA=(^{Fmt8nXF_XQxZaADSmXd4QQLeeKZYGPw zB(|1{v|Po{?Pi@-A*&j<<-y@gIGBgDm7Yz4O|;y-WO$=rD<?)Nz`uv>y>5|W=0->1 zBR)=UnLazv^TVmHwsF9!99PF2PRjZqXbp0XT<7!nibI4OnUN^r7(z%;E?s>%jsO`d zYKI2A{O@y!-)T*IC?~DLK4qoN<2LqTIT2j{Xn{U<N~#{QOO`NIPrcuIKoF=}Eq!o3 z>JHWSd0_!YIy{LE>Iu-`pC;YpoolOLc7}nSTa3SFV0$fi(|CD;)2;CvqzLEvLZHuf zyl-i4+o4H^qWyLBB5t!@5}3UgA6b)w2$i04y+P78osysOI)*QXmYNFy?nf`*?P?%= zscSjQch!n>3v)T>!>N2g_*pbgbVhDlsYJy&rX%KpgfWI&CP`)hv<W^DV+MVQat{k+ zyq>-AJ~PFMbGTHx?9#C2QsSh?EU-P8gP|H)e`WdaLx{>}wIpCnqS9X9!PMTD<u89M zgZR@w+9(o`q4Q6k>%Z*!f8{w2D=lsQ$8@d@<J(88pd_*RAEWW_pyq#D_84)NXuY@` zvHxS0i`O`zRG{UTTmIX@_+!3iKbs&RRWIjf`48(g(VLx}<tH+=!Y(h#zYXhmS%ZK1 zTGmR1|M4&|9i-=P{{5Xu4ypK$e?<ImV=R)$&G)H|G3od)0PsJCwlyyfE_4I;1@^1= zp&w~nn!>;9a}MfiumgNpXcA8&iJzYQk)rSW<CDJ_N$P3RGsw}>UkcbKA&dL$KL+^^ zYD7;mB)S!}y+sOOUrK5S^KeZ|lirtcN3K>dE;>LuNKURyinzpIseV<7$`t8m@b7Y) z$ZLuv7(k%G=W%(QKrS1n@<~16B3HB`zHXQ#o=$et)oYd&fj?GNB6=28(Pw01_uacm zfiM1&9K5Y`2EM=N6J552eKSj*&zPzL!$cl$Ll(MT^jO7}2o|@_xPb_AbMNxT^r@+F znv$FKnujg?XYShGQEa52l5ZA;53SWUKi%*kl6xQXIh**cJ${o2mpj%;)s6eB-|=+s z=3iA*>e0D$r0YFgqPTf^w!!=`06)IS{Zjrzjd#!+fC;u%gN%h7*zWN)7L%XPxYz5M zmD(Th2ph<`(MHvNsO!J((<iKw@cGG?IxgGLN}Ptqa&0F;nZR%ggF8(6ZB83Lq{S7X zj@3#zFb%Tk!xJLjsI(B7(eTXm<^$`W2&YBD=^W37Jx1o_I~}&nx5%eIrSEgST=xnB zOY-9L*cVN{&F=c(%qb*x=o!)ZZH0e*xBM;T4<8Ih%5^i@YPb}%acQvq=!Oo&<?3kg zCs~hcJ;=rwHffo;FVScgc0x4uNQP%dygAFl{5iq+VSRWHsPP{3xJbhao^(TEP$Kv) zdbj|}HrgWcX3e5mEbLI`MVcKW0{M_o@?XyDI<W%w#?hX34t1`XhI$!H=69dhGWz-B z7CUxaYZ<NG0oBkeH(k#eA8*V!&rDBZc+cLEW8y(U4nGlXyjY=lHl|+guPdd0ku=Rm zd1%4`^<!$`Fj|2`6(7>v1!3RPl}-zX>7i=-r*`4*oUyHRI<wNN;YKxY+aaUMxzy90 zrN&{|gN=h;NEIbQuPQPBFxU=Te?Kjnj?ttZ%1urFaA>>vrz3nUN#burcNjZBFMlyY zFa>jAX#O2%xdFHaeA9M@KVX-cmJK2}ZQ0b;K;0Dy+{wW@x`**1MtU9IbDO;Vm%n;l z#8wPgG|g9O7QXO!ajX~*fGfrWr5m)FKFwjqA*P@Hd@27Qc`?93cyY=!^PU!#B*hy2 z9l&~N6n{97aM*Bmcb3+ICyMlrh74^T$m2Knx#v?jQ2_UbjT;T1*v#*T9b1#zt|{p8 z^XU<nl)CAjCZWjSlemE>?tjGj#CW&nEn{{ovy}?*``ReOHo|}eZe?4fx@EEc7HSh^ z6NErKdS0Z$4Jiylk=ox}wQ)a5<T_`ew;SaMf-ekB&`ljoa>b-B?hLT=$f7IjI9YSJ z=`W2nmAMtcvZ_D55DDJjU-g^<zpa_Bmmnk^SUY>vh8z?PYc6BbYqw0$BZ^5B9FZv9 zdXSWw)~b+xI;hsq=5kq~!$SKp{MVx(3>sB`tD_T!+_z9l1&y}qK@Cfp_*eq0ZW@Mv zAq>U@gm7N<2ZWSg2~U33k15W|GhGrYBUu3F*GO*p9-;8Hq|Wf==LYHA=xwTgnJe?w zC@bUz&$;$mnL(3apxHBY(UDLe1W?6opEFS!xfE+T_YP!tc}ntZXdy;St`<1@vbRi= z<Y`%i)*EcCMHz@{ML~M*{1#=^4y3C!<|?FWAk?JOmV~gigkEP^Z$e|$l}L=`x~eOK zX(PZbM8o?y^yksb&ID*-Q<g^t=Ja=r8HOgQ$KHUyJ^c65sAPYsbY^nw4a?Q5baR$Y zQ4LqQB|~o|Xm|R@923s4Dx6NU6KBHft<yHb4o9?%B(Bj42$Qwb@m;Q_25g;r1!CaS zNn5}Wyo3H6XkX?2G%{GL!}8`gv=QQ4T}8Dhwrh1R1V(h(FxZ9t=|pIR=t9yMEt)!8 zAA{cSP?!<TVz!m~)13s7B*rR5uEAx30eS^Q68iqrCn9*ac=xjRLB}_X@Fz#8L?KL0 zUURHa(dGQ_{ahLfXSB?r0e1-fJx-{is{&EUku&&QeZQ$F3q3<_2z!^ykO#{R+z1h8 zARiQ0u|$b<#m~`vK!2Y}$0ruVg{F$TNM8LBuI=(j81EMU<hlCs@{aBdj31E0r|nOe zsHeozOBlo)9PPIq!GxHr$9u)l$ef$;sM{$5@D+Rkuoj6NAzaWp)Wvco*)lW(rW)#Y zRMn&HRhG54Y`wwybaHi-gY-%}P3bY9{qOVdXHIzbUuPv=YS=UEP0L*I0`US|^mpa_ zYE;O*u#ZJ4^{9a@OG6!GwDhAGqr@UhC;QQ!o_W0GcA@U{q})6b#+3L<xam_2rSiJ> z{97XRkF`q`K4TFnoe-z7c<@akOdBHf+V=<K4FdrUt5a!pf#inDuw;rj@**ZBJ7uNJ z|KF;x$S`l@EL2iTdJ(T4A{vvA%?~%92^mXGf7t%i;T1ukPN5DXZ`mm;&JovLoX-+6 z7CiI@r0j*QMbk^_Na}mP9De=(cIR{Ji(hn{Gf^5AnOEghgks;t2CH2k0w?(jm@Wy3 zc!i-K!{V9h0`x%pU!h8qqjr#x{^(CO@A)a6><d0Bak*{nH?wT_17X42Ci?t4<gk?~ z;i#Q}q$0h2OwASfnUAS>H$6Dn-0zVh0MJ<;imLM>hG3cPlGg7TNVlvP?dKn$$KJ0* zzIXW#*!$q49#APbj7e5oVRfN}yEKa#R^YiHyKpy3xS4s|h?OB`lXxbb0h$QxY(XmE zC|n5YMIoDT;!EgdVMmb}s&~FiNej{b!oDqC68QJ1X!i~Mmp9K1!=5lJX!w?L%!ooS zzdo=8W7&Cw2Imu?<FrY1AD{usa4lne5jW@;I0CKpy)>KeB45~dwzNTejqD;G(}$b4 z^`e}%%yzNl-)AtK<^V~42*JH`)zoBTA{c)dX_StAEN#0)ByFo=jGbL#3WOst^7le@ zjyUoBY9HtNM`edL$nn?4WDQnMAw!@r9V?@Z7rqEoEOGIxC&&JfqPQ{Vou=}2Lb}-q z(Ge4i4h)XyirT6}3~}=XQEP|+mL5<6jSv`Ff9*bk38XGb*8V*1Atz^z>-*lG;w(S~ zUxfEEcO_5Dz{B}>#*lvZ$hMY%7N#7oVvx<*FO6Lv`LS4L8RwF|GHmGVIM+NE>C=d( z&!Z{8YGbC}moK7py&?N%87Q;BS>@!k@eYSp+Xa@0eyyp5LS?AcvEiC2f!K3U&h&#A zsY4Wfl%&P)1r8LwhOqn4g-(8Zq*pZgwt6*kZGwjjHauaIKzgr7k{pfur8t>Ku802n zJz#qIs8)7##;X0<lo{&Lsu`=q3d&W{lGV~<M%KI|Xk=c`&DrC5>oAn|DE(-ju%Jk} zM>$ZgFLn61oY!qksLkm;W+Mb&z3G0?ST+eXsI)1Ll|d#{6BJq;_+xZpUQ@%@cV7_{ zn`Z`ZiSMfnD@Oe)-rb4JqWngm8};FO6<3PEf8~-It7g<lRM0%KrT=Yb@n&Q|`yN4T zFmCDS#}XPzY&@$avF2V0w|wKWVf~dDC0q6&Qr#zeQL`mRft=G>Cat~|Geiqbv$+U> zM%(l&t}5g(2E;^<en{byLZ{-i^VhFIh}^qpP1THAc)&~8bjcC}eThMZ!ps>|s&BkY zL-kM@p?Mw_6A9{z1h3DpsP}(k4(8z81N@#Ox)KF&G(m-fmKPAzN+R&e1#Pe+ke@>n z{;|)udU_6R;z82rWfzX{@+C}GDJjF<4VUB7%hL-nWOv1MK_@YcY>~(7i+=N%5Y}5C zSUYV{ZQHhAV+3HOIG))RsF)bjvo7Lfe%Agts^+E{x_h_T?8bS14c+Ld-kNUFgAuQq zWF&I2o#Z~s37ZO2KAjmTe{c3hY|}>1f$Md;J9t||wCOg*k**Q~tZ;4^`7K3A8y>eD z_qy2Y`#1ew@5TGELUB8Mdj(vPvuUb$0bEA^(+}&cYI3ptQTrdMsvB=Ru-~kM1Hbt? z^Ux($=`Cx95WKokXozpaH)Czv3W$pmShY2mP9Zd6obn^&UlXvZrH{$%`n@X(Vwxm1 zB)UP~VTZ|iHAtVJmKUv6iZE5Hf@8y+03JMh`a#4VOJ`eK!l-a1MfrYwEHgSC+BuA3 z6&CjUC;07fBj0yPwQ@H&5-tI>yn?8<wh(wFVb|(WF_iXrsI^sor|!a*X@D6j1GX3t zNlQ!c##HOgrt+vn<A+&u4^pFS+9o<urepOv^hvCnxqY00ol`Ic$Rzb3ie+{^FR%91 zL}0-W^HKP2EbDs-kg5m>zj}<KSfnc<j9!Q~kxgaG8(>}+bnzt!3kUxNW(X#PXT>g9 z0C|)&>!XSxQp4BT-wi77m$jPZ3arONa|4wqFjqS)afe3GmY$iaW?>X%OBlnQo@7oH z&~{z}cGUJfyo?!hmBA}_`lubg;`0bY?!7d#Gac+VPLz0TV`30ma#6+D9MZeGTD>$v zM3=6q=!rni)x*R3rSxG{g%zDKi$22Zc+5CQQu)S6FEogR!Alg|K<><DhHs2#?0cAs z;)0JC-y$F3`d-~Rhtp02lwEd6Z$~s^;eSs>6q?t}c8JR>Z4(E0gSySG7`tvbkexu1 z8jK0}&NU<h`|b_7?IEi~0OYK^tpuLb2Uf%19M5l-_%%eSyxa;?p%H<%l!{a#i^|X& zr2O7*;N|VKxf$R`zoUSgp9B(Ir+NqBMB3fg6Ed+r+g?~4%H^_s+6wKb;I#fUs`4uc zgE9&e&UAbbru|R`&L6jG`Gwcw-@>@`@k=$YRJFf|oiZrSh(l(qzYWdhsO7US4X)wr z{s967&PUT+`v6HTGqn3`_n{>(YnAqT|M+lKm#|X5g-_C(sY%O}T%Q>rVLaCez`l#Q zZ9Fa`u)kecnmk&}aHl*LUuOJvk#J7JSgZNUXzm4~AHj+Z{YvbRWQhbUJ?ENt#6GZ> zb>=)O<CcvmA{z@QAiJ<dOkQm`YAovdZM1!+#U4&wsHOu8TTn#oOA<%^L$778DN}h_ zZNACV)VTEFzOT?dyW({r0zCc2=qA&c#0XqL?iqAX=^-rT=NefO0l<okgD)evi6ly< z?}PxQ5n4DQJn*gS%BZ(TYvjG5QQyqAfXU8a#qu5+eqU?hUpM66IdmR!C-Hgd1UEAZ zQMmjR(g<&<TAY?RkF!@nOEA(6R`PPTIaJ%SAH%KVPwKNPQ%rbJv7I)G7jqCyb=Pd) zD3~#eaaxC%V@QB5XvU?(4m>G@BVSd@Jj;tjDzW~8J?m%8vrrSbV2*y!&|nT%@Id78 zPC8ZN#RGq%%QzCVe2J0Y<V#A?^^h~Y62tbk>Asrr$zGfxI2|LuCVllXnIq7y0oP}2 z0lwLRRa{#*xX3OYw!+rA6hi)5GHdqP0rgJMdCe_7(P@ZVGn8W0D2F(QW;wTQ$od`1 zE70cvuD94bjWBhXx@@VyOp;C5S`>@!a5(g=?Fx_+kZW8VutN}Dm+$!AQ-YGg)7P^T zpB`T|U-gX9RmRLB1fB8gb}_y+K$w&n?GlHUQUf`0+b^E!kLN+d_*42B|3)w)gTU&S zjBmyQu6PDz2Cj1r;Z_yQU_-33_1Q%u+X(Rlfja@>Fd~*n3%Af;U!0n-M^{HnD*Y|Y zoz!cj@$2ssvId~7jvnZeWG^^)3BA6y9Ey=dX5(RFA0l<70B69m(B-M%RQMC#7oN)A zfv9al0i!2?$nMndJ-q@Uom;zd-W1*xU1c?pkIRB#j4cj5UG7uvgN9U-w@U$MKA5ko z?jPq`kFEiw7bojEX-iH*?FmF!?U-^2NO{ciIsp_;?*aPIQh=TlfW7;|EH*Y5z!>oY z{fF=CoxBtzY?dDh%RY6jo*|(~d-rCzu~)udC5|8;A+>2)qT3RIHa^?G(>(e2)5d_c zB|0-0b0@e93ziS_66g+<CCmBq{>QlI_?L#refN7`9B>pu^_^1)L_jD9>~`&b87bf% zmjl}+wCLW~yiaqTY<>&BQ-kW7P=ep6xl_kpl4{>cK-bW}b$Q7h<&L2a*$iouiyK6@ zw8Ly7#JC)DDx2vHB5Ay3VXDNg;T$7=y@HsWxST63SrHrRmBVT!n70w7M+!gXK(V?J zPcOq}<zZUyXh>ZE`}#V#+<2Xdq5fov4nia31OQT{IBmqi>*CsHa8Y=R;=#HpX*Q0G zfZUXTnd2nC7(!}P0hH-93~S;b`rF*mjlrqK%R1sRQtN*rMVMj*Ub3jV9tV0HClC!U zP;sC;X7PpY2;9LW8m_+xTgXRpc&NCY-pi5ewe3+tAn}^^H~eyz!tBHd!3^4#o3%1* z7=F>TxOrN*p_sQ3zSj6rUoM3$%Suc%Z?qLeG1G#kn(cHNFx@)dsowc9SY0l^pXSn~ z2{rS~biuTUNmNLb)1UH{N-S2a2(oi&@36ffX{l@FY@37cPDj4+dL;-PG?8i3fkHuT z%FBJZHlJJVGfq&GvNV2N5xq?v2Ye1p|70&gut#4qRn|a=a8N4pM_vGG$#i-4P)p1U zql%U=Q|`Y#eqAI|Voj>#G{mGE_42D-k|;hmE1>A+k-dnTN!R-bWEz!qCHNG^Eh(<Q zUr$)CexZKu>(WDa{Jlfj93@;Ib@HAeY;An4NJ;PI<=kSwC%NpQZ?p1QE#rFSC%!Z) z4WIFeNj_@1w-Ff_(s=VLe%}-y0x#;M^^!G{ac$*B)ub>pOM`DSBI&<WADWDv^)tOQ znb+Q30lI@<3(ntNUPc6>OHuG5qb#51tot%#N_sqO{;Y8O0iRIoXJ*WsZ0`ARFH=w^ zagoq<ro3D?)8LG{RFL6K=+?PLfAsKFdNji5bLp$FZ3=qzK+*``di`=+77ctIRzcE1 zDXt-(HYES;)7uU6D64o{LTN)B#i=+h5kBlVqxd69QHHomF(KG1*MX5=rdd~jL}zze z_h&sfLfbhiVuUdn48i+N!q2i=uTKg}pf}aFrejM)?#-u;t0XcR!`97j^%0RA^S%oP zKa(n&9I94E)~7s@p+t#s^}4kc*C#ihH$$5lB3<xU_!83Qb;cF=V*5k;PjPe@(du6v zwE1K*JVzy#!_t)6vF44|0!QhFaG_*UHieauY9XPKs2qppWzsCPYmR>QaMhYEGh{Pl z0AIR`iyb<lF)h8M?GmtX>y$Od7>-m7l)eMM1mzl<O;s1Jbd|)bcLoQD@89b0CrZj{ zxV13WT0AYUtLh9~&xi6Yu3L>&S%#T+z6s8dy`nQU_AS8`ZLf>j3L<Ew{ux5lO8lv( z4||<`-_L1#%amNiR=o2hh~fPQr=_A9PD&%*ImdC);<eIR$?wGblfEk?juE&|D5}gd z?-Pz})r(K|-o38K)=L$lJUpD))hI&Q$yw5eX=6$}Z2ewYXLK6)J?(O4r7_uZ-lOF@ zAf2-U2<3M=UP!GeVZY|O$XYih#JIsLUS2_NH@>>NeCW@0s<+DP!EoP&Qy;<5q8p!B zOgG=RWFS27eu*{k)WW)Pd?2I0-(cFb_3M&E>aS8{TF(pGzhz^yrIn=F_b_fJ&?dDq z-0|FAoz;c04O$DC-tp+UWzBzY2)pewaB)JmYDmx53oEVIEAErQW)TVLzqGMC0L6i^ zqIoHYZ?>MENmK_}`!8wXvbT0q=+WW^cyk$?-ME9++Sb6|-f1+Hl{Z-N>hK`?_L5q` z+XAkHM}$mn4l>_5F<+#by6VvhWBq1mWAqaSsBgXmY;@{Z*X6QZ2g72H>WI$YjSGj9 zR~v9raMdveY4iqI>e*cf0M`PRz`-rvnIM7bL!G+Z+aUxRLPdAFc-LLMfAw!Vr68qj z02i`;>XQu|OsfRPEKL%TAw%v#OPxQw_z5<jN;Vo^`JobT;D-I`!`(hZ&Wgc<e*RLy z%<NsBk;9Dq6MV)X^Xtl34q14r;e7py*<r@I!1OkL@l?rdbUIxZ^I)uZv=;?aB!Fi) zlAlH(*D58FsyL9c-_Nbm4di)&u3qHbG(({jJtx(8fL;q=d?d%g>Y|W7_A*d30D=wo z<`3s_N*P)Wuu0z*PPn@E&3*SNv8oR33kSuhb=NSlK2=YYd+qk#ct9B1v0OV7ENR`5 zapPj64Txno($HZr$?j8GCu58YrVXnDj>%_c;Qnf@Y75wWbDVSV_~}s$;GImKA{6$0 zPo70SCw#rsv)YpYNgnC4YKS`ty~meMFUy1uyJI0p9Nq`C?XWFTe6bOl^k~x+1TfOQ z({$%9ewI|O*7Lb!XePzs>n)C2yN5&9E?xCy8tC$3scZX(dc+vgfMZThqA#^lG`TOg zCKyv?owryJGAk{)HM|q4={DhGKeZ_g4)p=kKF(@1fay)i(QamN*Ki!dnEOxOTfNXw zA~$T*RCJVm@tJ977knFjmb-t$pT92!nA5GUX2h6#JnDp&imc6{8hTcBB${HgpX1&D z3{Y2if)au`bIHq`bW!?<E}BmS-M(NC(upX3yCg4`+B0|HEY@@g3qTK39)3)^w<r_K zEe)SA3eGD;!Az5HT$$+beCB>4Jv5S?9_*@SixngYHGh!RsFVG?Hv>avo_VWF;bF5m zJCuQ6X;>L!k}oS+98rumie;;~?dcXGuce_yG)-%I?{)EpD9xx-Z;4R$Ko@%u6=#{F zP3xNdhm%T0Vluje#JaBc>!tK9r4^(@8m=4z3|UZsu8q73vU7rv_5#-ETU+LBfiD4k z&T6;Ms=2r%9nT_%RSijMIb=aQ?50nLiDtfQf(C+R-cwgBhkY;XUH=#;tK>Jou&XK) zJjxO<<o+*4)}bPx%{8;ZVL*Ntdaz}8V-%Z=)L|QZc=84b-ukY8C>b@!e(L7<_Vn7^ z?2xb5>ca)eF&yk$9Z$yjSp5R@N`|dLQfQY;4$HlNN}-M4W4D+}DJzug#P2)Rr~E$q zbtTcb*8=GiNGN)6$Ym>99-Ko5Zx?;bBB6?n2&*f$pUX)am!<&Pw#V+aDWl+JV>HV? zaFFK9-N{a*E>U#2cpOcN(Md;(&ip9)c9i*7bwKYt8SE1Lp@%g+r~C@yoKli-SXLzS znQmse>{jrZchmJ+0iwj)hLkcX#0Fh>krzl`xo|#}8{9)Ig$lXvTlp+1LvCpOI7|>o zXu6A6sP<fVij7x~iUOaNS065ufj6%iSB+^BDo{wZf<lJ%6lqveRko1St=$?C&;;V> zkm-CjcZrwM3^ah@&Uc<eE?B!Zw;i?vNMApXn$Ey_E$N`WZaavtNv7*Oz5?m9?*Ti< zEGRYR<=PZqO$)hsdB?dwkf`*W1C~fk)TwhQ7i6MJ#7fyTR_9Y)3kz<IZ}Xvgzee`- za4Pfqi1N9cO2q=jwj*k^oV8i6<IR79h`CIa(Hc*-xVNZtc?7gSgz)@b3&177g>L&& zkw#J9?WXCi387hzywiyKAgn}q;vRfg-yGk}T+FgZ1ES3|8`T?KwrDGS$RkINb7ZCd z6U%LiI(j4A5l=gL1v_JUD)F>Ui}YF1b3`W|YJT$*X9+J7bod%jIMoN{sah3AJ)rF% zK}qomrH7_^Ep9U<#jzFSJ@3J5c<~~bwMokTz-JS9`(FL}ywkuH;UhPqJHP54D9n#$ zOPb@YaeN!-)=N$CX^1$L=7WHwtaN1+iZipeA0}`9{r-8V@X&{Dq?ymyZ72$+`ClhK zic4ww09$|Ee}jt?U5rHwJau*pabMeiW<KzFgkw0c<1YBgzp5UartZgxZ62AwA#$_t z#er!~*#%U&BCJ4zho2W{$>HIexOP1PcWG0$6sRD8@P}HOeeOVAIES0XeqPB*LRby> zY;x(JWDa7a6Smd!K&1QmiU_&)#%{q4ED=~wUw8jWd}5u%u;GzP5093p+KPpS%U*>R zZ+|wFcdh@EZZeM@mCLr`D1Isd_!@@zWye^-SJ~&sEXvbQc{{B`CTY9lv0wbT%X<%{ zvLER7{ozIavXWN`N2;Bh#vr9&LIuVAGIzqJPW!AKGn(qR?OaEnn$4<cF4{DYXb%1? z4td)b9o1k0NB{W}<>^m`JZ?5&GCUvU-?g=M8kVmIZgK;@+yu>n_oJY9%T(KVm^gr3 znKhO_D~hjter2cumr;~Kn<Gpf;SopP`?gmS;kpLbjaAYXOO!G{)hQuCVa2e)%tX7a z%`meFT@w2BKvwMjX&v@xDPAOyl?Net2V|_)wO9yCke)RzDE2E*4n^F$lUjhB;4S(H zjW{(ChwPKJD=oFnJO`Pr#qy@IgG%+vCfw2{{YrW%N`n!Oc_74$J!RuhALDnErV#nx zh+S><mgkm{REE0FqaDNQl*{~wt@t+lHn;~{QNj~a_XyE~iC%D23+<5Ko8FV=K2e)~ ztj;<*<t_s6*qsY~f_<pv_#(i4)!de!OW*(CS(k&jc~g6uJ5jHZMlWrVpxdj6r$3Lj znl7Wp8b(5Ve?NaMRFqiA60WDaP<((^@l=uOsU^ZdWO{XzH#x9z9Iu~v{{`TA7w~=m zkzWbFRg<cAy>{KRB|->Llw*T@YiDk!xJtfV{YXL`iwtI*QTI3N{9a!6Y!DG`tWu+K zx7e7<zhzINOt5>iPf%&F9`Thk{6ETTHxuI2`1r!Q*{xFlT|WK(RLzz|N(J3Zi7hGY zPww@PP$DRMdN6<g;QAVo-~H<X;wlpF`lwn1^7x0*&D>sDNc85p(6rMGxdcw?n87X< z;%6Z}2AqB}E+o!*g}}2X>{H6!f0a}G%o<Q(GaZ*nvdBd~@$~^-^pbianayd`O2LTh zFWGBNi=WfUBAB?!vvQ_ZRT(-$2z*%PrMa{)axrCe|LY0X)U2Na`4UmMrR0-<<oYjH z(Hd3RhwbnVd;6r7l9d(zuqX{fs&>7`*_4HY%oBge<J_yY|68B#<~~Qm#tOr0fAgv3 zrE)CqI)2~1Q3Mt5;ihwfve<i~#@DSvTHh~gOK@@>)dPQ^r29-tpoNvrlVg9o&T0K| zh>;lU#f^b7lQ>Ds>rY*$ERE@m^{5rE{N*q50Mv&`N#t3<P(Ocy$U&Q8ay-Y=J9c@3 zuqes$jC<uDK&%r?Lu{1T;Uj-;H0tX>oj~0Bm<rH+DU$QB(3K1$@ap`6nDa!MeDGtR zr%ZX>$6z!AoB4)~nHXWv$1x`YAT_D3IFIHZR{6ucd}l-nzj4!rP3;MPCQrTISXDqu z9GTGD&5=ouv6;$ABtXdLH^-2X^(WBm>cm1;*^l+ZCwCZjqgE=&?8onC8Qw!tR*AV! zq;0mJV4yxJG#K(YZgV=hN=I?{3^GQ{PiWrZ)Wb&PQT{Bq`QdMs#)^heNo(JWF3~mm zLv1d0bLEZV{4X&Mass}v5pZ5L@zji>I(N((_+nP@h|;1o2?bU(^`Jq+#qHu97X0va z)He&GfrH;E;m$f^bhN7gO6E`gr8qlXmrE?B;YD@@?bu*Le}TUessQuhNWKKgFg47U z*N77AMzCpXX@A)4uiN)4BDwgiB{vDL43BER%1?z4>YGc<;7t$3-S(~{3*49_A1~RT z6V`JZnPNRw%4K~?VX}u72<h8|P3Y<hMlUIl1slL;b<kYyU+N13(9T3URaa4w%Nk^d zP`<zz|8H^EVa^TFh6wgGG(PYX>{y~TLXWBE_JpVhBZLMEsH#-?Ae7**<Q0KH_NIW# zX)h>Mpw6;pE@>2g7$f`Z>%%s6N-ix&R3pCl)_G**Pd4w!WSs>9C>tR^*y~P^`WL7? z)ws|P*d9HTgpsw!>jRMu*j)dwglBW#3xkGy9t9^V25kd##7+TqoaB7{mVI1^Kgt*B z?6cwH1LHQxcuYt5`7sWq_h$tK9>#1xwKsFU=9$1jBXx`T2Gb0j`nM8g;n^Ft6g#UT z8jECrL}T7wXS=l0!xpsppsfik$^rQ|8-LaJYf7cnxDj#KEzeB=I)M)>P-Q$mqUw3+ zpGU-zhgIWJ)=yJ`s?E>U!Gtg_7V^4kO`OrF$|4gICT1}KE=RJB%VFkD<ZmJ*p~2J^ z(CCHE`<bR}M&(UBM*YMO3w1|gZm%i-PlZ_Y$&xzCl6hv%Cc=&RLB1C<?YNN*<!+)s zDQdMmBRp1s-#8aYjfJWcP)=R*!ia)SP}O6+BL%VK)ODL^5!3!49CPMPXn1&a6FZ%A zGIVrwDrXm0=eDweZy2hiaYnUl(QN~H1;-tCo{t+jT+|0ZO*ZDvw+QA*$E?ff}y z=9;J9bkVe%Q2$|mMV4@8B!EeTwN=X38H3wn_v+J0LDg&uEGN7})wnR2V3egYs}ffR zhwaB8{Ld-&FRge-EfD;^fWywr;8>=T_v0+02C!@2GTknnfW>Bkb3uuhoaW+_UZ<6~ zWh4=btRQMB=6>XtKp+n$B&Lpn_~IuofUTaZKA6yka7w#yG`GT*(K%d5Vu+%%-9KR@ z(PAEZq{DHt=H#PAbHB$gkB1a<^Ti*}s(uo%3T3KDT~K+|cxOZG1<_`4D4%FxC)-q| zc6i{{r!(ApePqO<dO}Q+iC)HDPStezR75*dq->DjO!@6r<KfKSI756!gW^I-MNBGo z1xI5k#)B`yDlWr?^*Px!*h@CK-Lz6K1_e7L>ONM^D~^BfH+?fA-nD1wcw1qLCm$SZ zORAWdFWeW2O|sK-P7ok44s$RwLU&X1KJe)M7i{Z2B2e-#BK_=~nqYg5q0#79wwfi^ z>Jy>LD|L;c-#j?80_upy$R3d>4fK=ihs>MWiIW={8og*MW4OLLY26UsWJQER;;87R zMkuUGR>_YV(B@m`ZyI5!d|6gXM*ZEUTdl(*@ekWpIwR4J)P=y3cDCsss0+U<*JMU4 zTBv=4^V5h($<DQ)Zdk#&-M?|h!n4bdAo_0_uelnj8=;?Nmw=@8)k{gCkY@vw35Kb= z1eMg4$(75Uol<O}>cOHQitG5}_^U^}d2YL@&Wpg@8#}#`10Kp1OcYkkxR}_p?uIBm z1W;iHy|8=OLOIWDXSAB9xWv>0>3ON=<23vfyx47G-SO;R?{8A@WUAL<GZdnT6(lOj zxQOQRz$OAz?fh%~3v7U0EBla99-_&kq0B^43Pe2Ji`#E|c4%VH9t1K1TQ6Jl(`LAx z!`ETZsY6dCY50RQLyC^+ZC9-vpv;QlZ4QpO!;w=RoXMcwMRf>t6kb<r_1<c}iq!93 ziO!Qon=By$yclMWteQK?yn1<z4!u#2*P{=+6k+y*KBtC8Q6MTQQ(1;R2+Fyyc$P4M zf_?3C!4#1*1jZCb*lp&QrK{d(_3cLG4NuB3mspz{V`7&raeD%QWANpqw#Eqhd_joA z{V@EbTF3x~Y?dOia_)q*O2ctUKtm`+()O~w6IENk{j=G&?d-z$su_w1-^eSQsyw2O z+O;Lie6VApZQizR?#wj1qowe~C~r3w`jtM{2-gi%a7kdmlWK<x)Mw;9ez@f_FE_=c znl5~a1~SmqkahD|D;W@Q4i|XlMo_GvTN=hP{q3D00oiS|36=}8Yz%4)8aHZVi%Xgd zJWK`!8_oD=a<}0{6Yr^9cT~E@mB~_{w=MAEJDl{2yYVr^T1|k5FBBbLQK9mq36iQn z#;X_01YFW~d`<GkcIT$nE*p*(!QhsuTgNksWHTCpFQN`YC%J3y*MmjX{<fd4Kc8tK zEq{@@dp-n`9R}H?w{8&(nhWZxhF|QzrQnT(H&N_iOb=GgJSn}ZY{)l~A`MKTWfR2? z6gsR5gc3I)5dE6i!<J<`3w{ZOTJ%KIeWL5*hPVTkoX=`UZhq@$V~k6!F*_rj7yGO0 zG@d}Ee?Ef3*s3j7^v5{&uw`tMA5_jfIbn-tVcGNTtcM5KpgM<`nQZI;TS)~tCaEnM zJW(YQe?*9WZjPGRDC@J7FGX?t{x&q!Mcs2sB^^6JVMY3CnVV?x`L&AMQ=%S9E)o=K zbZdAOig6y-)GQ_er}XskI4Y4~WgbY=^f+S+(%UF1QkR*M8AW8wQTb+3=Kau$aee5E zosXcjdD0z|*%HMVj`3@!>CrxtuG99-z(zW#muG{w6k$<xPNlC6PI;)`%j3Qjnc!hk zo0pJuU!I*sV(JU~)F=M5!(-{NG+3Bb_Ij)p7A>tifIUQD->C-PI_GDBbTzTcijVg^ z)dC9}$Bcv&=RW%Cre#bYnz?>EdGj<#;O%emA;~VGd_<2X_Nz!m&dNoKT5j8{K_l$T zy$0C^^{|6XD2|RHg?p(7;C#+&Y~oOtkyn`U1=QJsbG=%|M$g7cPt_pn@?O|+C4AV> zkm21XxLI)f^G}R%C*!Hm7j)RV>H63elSIVa*_->R3%<T*``b^BYRJ+r9ZZ<_u(4wC zXc_F#XJjP{*=3-INMzMGm6c<czv80nU3Pyw4*PsU?gOm9nUj1wwkFvRK2Pp2WWXQI z9#vK|XN(lcL~aCa#f+GC1-4zeAGPN~Ah6wwyyK&&!zd|@S;$ERGkv{7di)mt?6a$f zL;SpbAb)%<HFVEP!m;hbsyV>5y~GT4QQhd)*a&zp*xI{H^y$PQHa*~M^D9_%G^J<V zS=B)cmy^k*#rWMg^Tl4k^wG*E_sJ#;T3{__%xV+Lvu_66>ui6WbJwWY0=_=A_+e^Z z$zgZIzDrsQTF7r3<j0&X!xIneqjr~|PE$XNh_#uj<-pnx+;$F*K~GD`i`_2bO7UWp z2;wD9={Bl3=d^e++&tc2OIMj0{UCpXdVD+Hns!yEaW+KlT-JG=K#{rejFr(=`|$VT z))$IIn#4!8)X_aw)v_*$;rf130;fW{ug9U7k&rF^n6&ZAfQ1oftK8Xq(CADiZY$wW zwb&)J-3cmZfw#F!^Fn6MwBwTBjSnhK8T~JkiW5;aRDL~t%PI2{S&am;X{+;PMS-hO zBqJ1L7o#PIF1>(53@lYm#F$q>za?ABFz5n6j#Dla`}lL)jiD%+3|S(88fLGw716pg zO1nFsb-K?h(ZENM7yE9i$4jg5WOb_PyvrN0Ea>#))RH<&*RL2gNw-4uSE}~smK$f| z*Vc(8UkPa)eI-`?P?}IoN)%M>jH>KkCkVP7p8&5I*u$B1<N9bF3ArGz;$6RDd6&(Y z8rtH}8U(p5<vC&xra|~W8dS?bKZH>sn~jSXZ_mIPFs*(VS(ir~m6u&s#zlAk=>972 z*|i>0mNUa}M{pf?m4Tx#@olL}1w}v8eRcrtzBIdnRc?4r&lNO}l?-3ZB&Xq|ZM55k ztb}dr#4FJ2=4mge<%>U%JQaS?q}9o1eKF6+R3>gs`mQbcHyL8ENA{>J5xwC_$#~C~ z<9ox=5FhxVYo%nRj=mUaBH1Ul9o)QMWLoS&xSX5s^yD->PGZ-97Uou+4lBr<77~wl zY4_`%2UZ8nJEwL<O={%F=6m|RN1UAY%|U&qudzl6P6-C1Dj!?m&7-W0I~LHPaGnSu z6Pki{tefCd{cWBoRanF5N;*nZ56%h?zlFgu5#&ohlhpljb`Quxec&PYUQ>q6$7#Gg zbY1T=t1N7)V%hG7k@K+ne4v)hVo+#?Pq+Q%^c!=Pm)aX-r6C9z`1a+z#(|~XeC_VU z*5^FE3?stXN+hG;_GG>7Mu>jV=d(S|%qyu*sL69$za5$NV8t#!)AowzgWLbtFS_=8 zR2fi!OI`fU(aQBh&UVcba}P6iX=cUSvRWb|iQnZfrw%l_Xth}U*naMR3<0!YNjMlw zH;hJ{-Mlf<5`6usV-B{aYtf&R0f2WAC)=InS~^*0VNCFfSX$hLjfDi!p6`YkBH$>K zmyG9*Zc(j-()!Ym1VGWH;kwcl{D^9ncfpz71pEZH)M>K0(I*(LJ-ffKKVYkeU9lax zwzJ9>EG3c01TF=MHhX%iZ#P6Yv0ipmyRtr;RZdTNf%y&2GS;Mp8@osT89<F&W3@h~ z&A{!`TK{-T{rXd<+i^F`vBY}KoF`o>-qf2Zmx067d$~BOj`9xZ*QmqlqndkldMAQg z$gSGJyVqPQSzmqVQ@fu%e>tGc1bRs<>j{w%G#?kLjngGcBt6&C5W5z?A9>*Ex+L_; zTu#G1Q|B}|*L{-sEX9lS{<h!8paaAS3c?T0*=+}HW9B+fD%$(@)FyKzSCkXzlhhh- z1)=;%P{T+?QN1ZdtHuNA)LF}R70Jhwwxf}1zIvho+|$8~b1VYYSx`CIzh#SNFMEn1 z&l!^mT@2mg4o8<HPv#6D@oDWHjnBYvK&6?(7;PAJA6>Ozu#|QPL~1)crQN(ZKIrUh z?xT*Uj+f!<ebO7eI(?65yR8BDmZe;piXr_L@T|{Nuo&i-;=6qujBcwk-L*Bco-c7l zomzVS&H?W>-$?j?-p;SS3wV(dv8-=i$Pj)6x*)7lj{#j(n%A$&yPY-q85zCwIyGhT z4aVs;R?g<3Lf5CUE-PEeYpF9*0l9)Dx~B(q=u{YBReS4s(YBxSW{$!$?-mQ2KQp?@ zg~)^)AMvR3Ole=NZyxytGS`qXkUT>3sK2Ctz0`0(+F`vQZO2lXI^Qk4dA#wny7>%Q zF)$~7_m`{keq~4n!wP_I5)}I_?C#qgDmp5Tg50FQn;xFb7_l;koYGBX-xzO1^)p7c zfFyxGXUyV5^F(tGR0h)Xk{~c*^<A6w`7MuLIV#<BJ5lJdTPO2^MNaTz1}4Fy^$oI* zRn#|XmZPM(N11MwDW0@7FuYbj`pOnMG0|hcgmHD$an(c5fQ-7M_6|zpTr0#tOGlR@ zVgpon%%PGpN8;{2xjFC{j7t_|FT?CptgNW?2OB)EMf#+v@WBy9ghRA0lbVv8dA_(C zWUT2MUKTDeFN$vPS)4W88f5?_=WE9md_N|;^(b&-SyF>dKtlKqtI4SKyNNfLZ{_Bb z=aZeIoeFPsBML4Z+^BlR$%Y^J5lz1W7cX7HqU)aKGY4Bj6Cdyq2shtKSTdA(x<8n* zuB(OMEp+>wuh=7vql1rXAMGAlWh(?`sWX^@9d0wI`nxVW4&*aX5#zk}85RzN7M&xT z*>*55deZ4KSHzOVIgl%;P%Sp7SfbH=@Kz??*$VFvPZT)V`r1u#+IYdX#$7BHcouZR zKlxJ$^0#*NMQ5xx;zg2rmWq0#6|0qk_OL5|^&wd?rFu64&d_r4crKSa;2F;06;;m9 zob0QEPvT;7jjBoo%bo-KpF3v4SD;>g-QVgiWOcO_r!^Td2ik8A%H9?vitqcqy&9fF zhGtv8sS83{=4$`gwt}KwwrwyLG?0^IF1o_dCVgeLbB~bsnMC1C`1M`NhoroNN};j- zCSX;OYi1Qq6Ll-KNQK6#Ulaye?W)CaS4&JbQa@(gVO(10|JT=BhQ+aM;ku1B4grEY zAwbaJu0axlyAucm3GVI^oP^-gkl^mpNaODAF2SvFIlWf0_Fntk^IW=r&Z4?zRgIeU zjdy&bRR~6vQjxRkkeWm>ZXMU}6YY_$hXn{XuZMIiAYZ*A|Kxg0^okjkoP3_Lx(#DI z#9ybKcaN#pONf@97VlCodVxrggsdn{l8g<Z8|wS}Vnwl^K&|>M(0ygU`;K=|VOgsK z0A})Y9Q5*`2;&G;w7)s2yM~^D2@lxfOPF<mp55g2PpcowohzF^*?P0gTpE^W*7m<7 zC0puu`7ZB&5aVDlc=L<Mtj71u4Y8W8`bn4&m&7>=uA7MANUf=|o>K2&b+55#Xm#OA zbE)<AHTxcEoU|M)bMoWjja}JhOW!ym+P4VYaLU#Bl0?O&H{$O2s)&5{Yq!Z;0@0-M zEy*4-&v_iUKf|eSzajhLMxdc-z-eyH{Nk{Iot^QS#9q(^Rs(j3c1TJ{r-30OdiBFR zmNoGK=V7KDi1fhI$~OkVP`o!T7E0}o!&wSNXE6-k#T?Vsy+_Ag7QH*2v(m(3-FbnZ z$%w^Ric&FPoY+$m68#_`H0~qp<O!C`WKCbad$1BLzC%mn)JNANh4>T{gNjl41O-3w zVF!dxn6%tUfw)<c5Opw0>d_q}&oV~tkP}3}LbhG+>{^X14n;`zyw&S$(p$wBEUYQ5 zHc$^|Y9{8rP~<$=N^0)OcFXq4e*J*3OdN)%lD{j-Ih3OZG}|u@qjH}0(o?D-JTJ*! zYtN3*i$vDJlR_>Jvi`W(erZw@HIHo0#mxb$niw^{*Pl4n0Uq@W_chb-EwhrUT%y(2 zIFW8lBcru2>S@#iV1VbLIv@CU5k`Z9rUb{ZYt4bqnT6*@)_8nep>waXi1I@Uv+Qmg zf_?^p#TJUa^SpskDzs56>IU9>7G(xy=V1<RtK97DklNvv!Ap_@n_AD6;gnGeo4jP# zx)481|1!kV(8U6NPREpvBLp!0;UYq<n`h-AHzZm-*~{n8JDiVbx^ea{B9RsyS!;<? zgEjb&i_DEzgznLv!L0!#xAul_`r>+q{!&8c?RMB8t=Lfw-ear44(V`A+KRZt=y#c~ zE1~_IukO@Z(1y&o^rt=o5pR%g5D!DJLKkeU>)<I;c=a$-k*VXT=h_Y*U7^!0+Z5Im zC&Cfcrm$eSQOHdo5gw2i%oV^>V$7kd30#twdFn%Tet=0)`E}2v$tj6NX^2whQ*ya1 zkh;L(b%g1D_doAVZqgy;#7NSpuqL4M0zD7)K7I6Es9m4dWTSrkh~W+iG$bp0{zU$U z$J);KVQchN{nqrZ6sW<fQ3I{pyHqCft&yOuT)72~mavhG(Wg;^9p{&RH^h#<j@Crw zCB9cJEA6W?z(~qb9Iu|6%kwd6)e$aJc|d5Ur0bgi$GUUf9af`ikMPsgxn9*PEo_RL zY0Y9QkdanfQ84rfLhi`*!;R>DSFTB}yqNIbeJt}Mv5mOR-I9jb`A&+u`6cnDeDA_{ zSFD!Bs@3!0v6HU&;EmQjIK~YXYm1hr2A{sZufOPuR{Iece=$0PU{wp@Q?+74m)u{i zHYF?%t05QEO0vp&%B=OmTLkMGXFUT<R|m6<f!W`>lDmK(G-H-nTquI#^%<>7)o%Wx zba)N^F>3aWAC`P?#s-r+AJZ&IpVlX#OlhSmvT&k<F4utrKV#7nDHIHRa~O+@&%INR z9oLd>CL>j25MCE%daS<eZ>U%z-Qs)gbJ<^LSh&p$Ex5(d1M1W3<+ar;&suaYcZSb> zR7EUJjGWOshp2a`k4&M=4lB6$pQc%5zed&T(R-9;$j3ERUoQ7v96okMk{GvoYx=eX zG}OYwWX!ReMIRdD4Z(sgOnbH(Ad-1+%vNtSQ_*sGJ4n|V7RbCR_!+SO)}+{q4EC%m zW;HZoOP#&l7|?s-$|cPVu!@j#WL)KJ*#J`}l4?deZM=eM3s_(gWbX8ssXM!aE<94j zCX`>a6LJ%&Vu=>6qANGbM3rWCQT|{E(NW`y+jBA&GIBCu$gI^Q_i)F}=y@hRtKY@! zg$8k^7}(nyeV!~(c{>g+hCCFRZE8iO6^pE#oX^n?>*E89mCSU9=$8eb#HkYmN#6Fz z#`2AGeZ?aK>@OB?9lv2o47BgwvIs~_oxHk|7D6zk6ddN$i*0zxB*=swFbvd3?MHI_ z>RxptXIE-Z;hM5~eYx?Be!XpErR<w60fyl%q%DiGV>KaA11kxcjwM1Su?c8jw4rgh zgwo9iEj~q7`=y!#>gQ5gQe%6L_BMj;bG;idx7+Hlc&<`f;9}%>BL8X$CxJ+ZOkhn@ z<Af?wX;k10%Qmpjve3K`Z5h}^+axXa0BHp8iy*JPUBlKWKFKU1Eh|diqvg}*{2<qE zlV48XN&5mv!Vo12MHmfre`K3(Ga$~oo>&9&jlKQR&3(NQ9*x1TN!oqsTmf<^Qx`So zanrNIoAAdpwX=ld+fWr30{+e>38zsjNwz>>RgM+dipfo&q@$!oiKFwf!I~{r3hoBt zepv;l_g&vw?=?t$I@<D1?5?B%PvWEtTZkOi`jW~n!GK8yYC_25FEPk<duTbIyMVEv zfAXu!u}VY^xkoPAJXG|A54E*6!y_ecD&oZZV!jFbLu_CaY;gzqwUM-|g|wgdSuIrv zQr&kEZ9%}d>>R}BAO-~ik0Jf#$Ke{nKp*-0lxs}jKKe9$iJ`c`@jduFLA=1W4=;z< z=s@}O)r02VniA_)z!YV1L5&NId5&Ap<UcF`o26zAux)Hf`(O~T?(a0q;Wj|KfZN8^ zb5U+V8ZXZvXh48^k691zIdin)V8qjziWMVx#yYE$=wm$)JbsfCVeR-~tyJpRYl-N- z_SXA&20bh*XPo*<&7kJq$<EM(*yaZ3^VPiEq5tYTXW>H~tbxWd=DTUE<sZ$+9u}+W zefvS0qP>O*qI9kJ?|KrkUg=|XYt+ac20{gSc@{7FM><Oc1xk#%mcKu=oWoiR%JD`Z z9b{c59#ld2J|dFVtQJ3{J>0YII_w<*1zdw5(f(8LBof`#gR{koYqP5x2nrX0L5d@o z!ih+*X<EEb@N-Q1(YQg)Q2pG6bq~puo}4em-{-C3gsVh)j8)!bA)Iz#>1Cq_q654m z*3KpzP7gWPOAR9(u>rpyI0?^52uRR;j(n=TK1qboO{Cn2D8-=#0c1*>mp`~JZtbGW zz(-3_+>vAhYkF-@sWAk`o(vzXB;Ts_3DOzFa>9DzD%g&u?!B``WmY9sC$poZWG7^e z0t>yILpY<ye}nIY=qNYAnTr+6_oARJc*8Hyn-8`=j@v(}hn{shGyePvU#c%%7zh7T zR`$2@gX7C;{bsy3;RiCqcP+LGNn=90LmpP5K3AJSs3ShCS2qw}ICB|c9-mNw?|~%$ zN91TqyMvYA@(aZ6ywG4ZYs~*IW}`;+|AX25G)dqk@RchxCB}#$6R<on=v_>5zS(;y zznpVsnI3^Rsl9#A0$bRs-@iQ2Gj}9XF&&K;$8PWT8!EGT1g0Ih6;4?IW2H$fo`OS? zpqw_x0X*e}1xup-r}JnNXeV07$$Q=awU~&iIt*mAD5{=JH@fe?oF9~%DcvGZ8BX}H z>6qkF;x7>D?v}zA$FoMp44@t@s5Y8|p-UQV>%#*&%#MCcgHv-;ctQpj<(_HqEB+#> z&2{3Bv+z$6zIL@PBAZ_D7fZJ^4_4)#^DDI@NSCnU%+c7}NrbBrW@ly}h|!_4HPx)- zf$dx?JQmJ~;>)$s@EUxU8-o73B68au%Z{46tnuLT9!Yxf>@A5Zx#w*)Lk#S%mO?1Q zHstr2U!pLV)|L0@B=MZ=cF_%GZR2U9UwXln7U<-=1DfWkekw!=&Fc5W8(Y)#u>H=! z9KA6kI3v>)e{LT@<Wmp-WAPPLG#?CYnxXPV*_>h2QszUgj(WdFPDP2iP?9FT?hj|l zcg%qr*(go>gYYW@(@uYNWp8C%`B5C^vF#$7IAK5|vM_d2sr;k?veJ~=MCwY$n%R3z z!?x|bT7f#Vj^x(N8<955{DBp>!{La`eR-3N3ixn_Tq5>nYn_j`6dq(p1{DhtN{mcR z`t{_|Lax=DTJm;u^*W6KV(ta+UctXJi&w&hqdSz7{@w&6Loz^&ek`xM+OW0rR(u#k zRd7T;U>Q(Igwy+pJpxDgB&_Vx!IDV-=Xza#Lg(aRu13P&ODIiioX}G=A|@iL@#7Kd zXcD!*>k?ymq5QJ6k1kP2)>BBBgO*}kR<T(G^&@x6<KvL&#ZX$DHZ}*A=~sSKw|uW) zRbh*!&-Vz!3W7U6BF_~>Q(_a!0l2MiNu07!^Vz55xf5*uv)`TnWKI3F{z?!NFo^Z| zZ>&5P8fL1!NVBSPn(w<gR&R6DmRF^0FF1e+yjsD6asZ~Kdc|d$(+KYwOkUJKhB)i< z7S{N%#HdM*8az!<J(7){a*_XZ;SUe2a+4mc;_g%p2(JxGQz1O4uefQwmQxIDRETCt z*~5=TgdfNF;Wr+UEyOVqs?`{<9gKv%iN;CYNx7qhH<buc_++)905SanEkq4bk&AQ( z-5|$&X2-fd*{e_;@=GL#M(Dzuoi#A8fj9VSzUeSCuq|^P!r%U~W_~VCTK0Fs9P0H~ z!W_e<ETq(y9Y?M5w(}af+&JG3(7rYFHvxr{?e+SH>U#{($@kYVGr79X(ts#FrG?Li zn`OX~(5)m*TLxWQ?8+|xEsCp#<qa`Mb&s;Cm!r2T-CO|tkf39QAM<Pef<HwHtxY)U zuZmr>*6ftDn<5{03U{$Ywit*QyM?w8hK=rSzm`H&3%{em8AUgx{(f<cXEWnj>rfNR zi|3`Ig3$>^#x)*@e5qIS!DAo=9pU>-APoz)N4KB1)-RK;82E|P>1n{#s)8fx0{Joi z!!>~$<u$?eJx@_)ddsZ;jaLb4pnFcEr5AGXhfojbt=01PI_1B!d0}Ur7!Xoi7^9NO z0O8MZLM`X_ZePO|D>;uTG@z_jn3ip&_ai%}W`by27fvg-(>?a=<?l?+KrBA}WO>GC z>dA48li#|qJlKE+(MJ9HhpdPo{*TNKt1ZOmaqt7|=;lx&{L=JHzeK8$Soom`d_NDI z{|J#Pcd0{^yN^kBGq8r!+jHk8-z`3Jp7W0=4XITFXvqG<p%@Ovk0W8Kp(2yu7>Xcd z4XuZHppgjM#0-&QlpS*IftM3uY)IzE*NTq|=Vk{jkD?9F_3n?=8<r{SaF7}aJtrk` zW{0%%J|-i|{5kI4e^jJbJ#p9ZZqgD<CUjG*_vm{bCb_jvI53LX)gX@r&kwD$Qt~&Z zM*YV9<IDKDBsg-<28E<094V0=-!q0kH!SS5^*`tAdNYQ~-Y?@JD`k|$K}KqGdeH~a zEVNbX=*gjl?f2Tbl#n>f8<SYL#@x}+NOXhl)$h~2m|Xn~l{Ph+Bv;M2qqFqOVcT;n zJ$BZWK9-ipQPe<O5HT9q2@w_tTiam0G~TeZ2VuveCcR+(v-Ow3kLS!ofG+-G7{D}R zE2yqJcG~pFtN1s(WTTQBB=-BD1=AIXIOh;{VP(SqgG9-Hq8_PuuJ()~{x2rw_dop$ z-pV!UkkA#&I*I+?(LL?r#UOVR@N24;{|tulG~c5G8KwhXb^qTORvvbR6VecR>f+Zw zd6>U8>K7zWH#P<YtK<ILd9i6?E(lu{E?uSJe{5o01pxmSu{CP{iGJb5zxFNxsUdw0 z2Km1dm;Cm>zn1=Qx~iA~_21TuEB_(H|LvN1>=gbb8_&q-yWQXO_2)`a*r9$|MaX6Q zd1hzucU<^;hO!sO`?j@H;LwWnO#W?kBbd%^RtX%fP<{pXZ$>Tk-yGxbCmL+3!_MS; zVDzD%#2WSgj*bov)GEWiEqnXFCuD;vyap6r(NFZhv-V4pSzr9T{bk+1%`MLqWb%@T z#6Elv`rl*x9az+C{%vl#l}_x(`=9x=&zpF8_G&}CC|7;V2~;tyWrbpH&Vj1OpR2^M znlDhO;MbVX%7ifaV%co|DYNtoa*&1nttY7YCpl*gfW%7D?Vf%>%Ka$IPL0J3E8kT; zejgB9Y8*-ETY0Z&)}5aI83BZsZo7eS2e@^lG`IICZb4k6tcfgF14Imc-RD7h-#m1c z`dPGP8Jg}GHGpP1T?y#nHOkbybk}~FPxK?|HPIwT!g=$Bz#ldveS}Ny$@g%{)k$q? zzUTCq|C1TVxAz_r`P3K7R^|ag(fCMU1~eK1eCB*YD9!BiH~vfnh*J1JUE`MJP@0K6 zY~fV$KAH{tJW-jPcr-g4qZ`ESRB22Xa}Un7(%%VvEUB6i`6M!K7??y|f!#eBIw}%P ze_$f6TOvP7U{E$7Z{NY76|DkS^X?FSX6NX2M$g)M5;`7RuEZ*;qaXjpEu9q}bGk%` zL~r@mJj+PH{_IJpNl3u5Mrs+`x5xO$rp3%{<EaTUG!b8g@h+Rybi3?{;Vclv*~nE3 z@=T*DbNyR!gst{B2;+}0&f_FVRUiUwJi|JeGz4!3QLuqB3AF7MWJG<57|4#*4PA=> z%dg|=OH9$W#m(6{{*ONm6e%%2u5FXsvDuB-&G;W&97ytSwOuLsh_2Wu$z8|Mjfeie z<-=M>oz2>!D>DiEL-@P-i&t;w$=7a3?i>8C;Nu|71pUxa`C?LXS-6M^x8>e|QR?Uv zET)tGt(HEwt$R;mrqYh+D&u*N2Wu2}@a7E(rC-fQx5I1p-l%@<V|jp~(<2QhtS51< ztnYHCZ;yu^A)`^r2f<Q$KTb{|ei3{=Wd{jNdbi4G3fXhGJarStlbV4Vy99cB1UQk0 z%+fi6&v4U}6So%nRA=Y;J2Zk~*XBDPKiQv@VOC#_AkJ`>VrGOw(kv`<Q7uW%>j8`J z1n^3U$~c}g!p$E#bt5)|OD{sHHalXwYriQ~TeN2f9mSrjA4xV4$4-f!4;9ZD`DAw& z@Z;OOeg0=QNTCu{tJ4Fk(Uq$b$+nqKEWXZ=m`s3Ds>a3Fn??)rHiXrx>y@)a_!+w^ zISQ%^xE{PqpqQ0=Lrf{uwDPhr!DbS;kaB`;Y!IB5pO(CgyZSI)1YGos(KN?OLrfX4 zXw)uHK7c>bX`c2Ytq+AR=inHyHF*xcJ>i)kASw&oEAvR6VV{wJ-zWzNEI;+ZE4N7` zkLmg<9V6NcuM75QAXy3SbLisr3hushxC({fID#6G_}T^2kPVpmM4-Ig@}`8L;?2FR z-4XyiJUbRA><a<}lOlsQUidUNLEaE+_=WV^;pxKIvAg?fbT||*3k9wnT3F%R@3=>* z*~Zx)P-pPi^n<xx>1F44v|E~4`jo7iDH`-U6g67tD)(Sfw)2+q=%x8NRDJb)bB5yw zgB}Ck`a2OXL<nKJ=%5}-ioR9ZCwTR01tB!C7r-+8GzAK73#6pkC&E@+I~i>~Sg*1Q zZpaz$0Kxu5EN>e$Ll6UAYAA8&0rD)`?v@0RAQCU^btdnXm`!Wb!jd4mW1utQ{%>4V zB8*hy<ilf|HNas%r=Fxu1od(VdZ!X*c#*g0!C2Zo;;mBO{HyUIPl0XuBZ9et(5~<5 z%3;8#cqyzFul*cnkaO}Og<X_0_UWcSn&y|n*R9PR1O5ntyQd!YHhwI9F5R`QC*0RQ zCyuQdYXcj|jsXp|1w3`u_-|Tpp$;Es+6Dfx$bqe%hQxQ^1M|@t4tzH$s#ZJhJ474q zBRQ_LT&)#-m7Wz=zkB2bx`4>7vv%uH8&H3ZZuiB1DDe~@84}jGoYlA{@e;jmSha7Z zOWOD7+`8flI*EaK!`TNr|Kn9$-%X+tq2u=)ogg_nlWkIScLIuP#LUr6d<-WQMz63f z6()M|L@h-;<RR7*cy|l56s`|cz)^mkt>@;p)^U#zHE3^!)e8PmY~C7#gqqn=LZ}P1 zfuhCF>aE+-wE`D#_9LbV(F_BRfue*$M^`RsNP9^?h9ZKUk@hjB|M9zB7gdWy#HG6M zl!@Hno3x*LSC)*)NUxJ_I|odueY-sF#uYyocVTLQ7y0&<ePb@wwQ(=91nM*7ndRK7 zaaAV-l$Po+PW#StTUF&WrD*jDWN(m@eQIwvaI$w7VZ-2MO`+`kc?=9vaXZb3I}T_r zow9C1?!*%h8(hhZ8h(*sLY%KhR$uB1s#P#7QiKcL4zr5{9CI#X!U|jOA~}TL^Ey#Z zzANF+-nKHzTuVAi!uoVJ94&~4<Vd?+l73~rW_9wgx)acY)N><`<m{Gtm3u=&#WgoJ zUC~l*^Uot$vrhqq8mCV9Qtz{2{e#L3wZ4MnS-CVJsio3K%eabDgsl5=2i<{7f2@aj z(;9Ddov8*>)$@<##hlV44>m0p5eB4N;UvJ*{dLPOAB@{T9mSF_)f1WN)~yxq4B=U< zX41b_W2Oujar0FWbBz`)tHaA0=vP_wg{~gaF}kW*pkse>mMu&`KD7;{4<d`bxvLsG zHTprE=&HqE>T4uxyU<Zf<3|(UW;D#j&G~p0Q#HsQqO%;lJ3RK71cW@rm$kNHf1c!# zwtXKG`Hb+LK9S0GW5H9mc<M{PSEdBq5fXYDi=BMvS{y7)ETq1oBWaemgXT&F2U9wR z%Kunjh7}nX^K=t+WX*;4UGex}w-1DlxL*3xffH?YPZ_;3c^(FmxmN{yCNp!LgRKYZ z2$x!jUM*=>*aS-{oz9`TZz;knw4)%f7*tsZ@JY{J&G(4Ky|$NJccA7P8f&W{xcGj1 zb-Sa9j}wjn)SA)CsI5jyow;m)zAo?9RxR0}J}OAccwK<g((b;@|Lj8QR`a&!X`RqQ zhEzvVHZXRq))UlAXkXU7gtnHN&bmX!E<j}8{oGv?9HrnC6e~^5DUlIMv$X;4@&&|U z(%wqPBbsDCeqF|9XJ`LuXyc^gjEjPFh;WFcVssZOD(u+E8|iMmy=<M?v5Pj5Xgd_B zy;6#C!w57UkUDa~2-w^$<iy?czvLq?K)hT^Nbmq0<gg$>jm3~~?F=en9d+c!r2{!v zE(6wp!f%RlywP-sbIo1-IE_5pPW-uNX3MYxdWW!zpe78jsc$<*dtfReM~^I=rAhz_ zzin$*K$O}6i2pPj&Z^87G-Lw)*i1PXw@u(kn{P2liy-@O)w9*QargR3Ch6w7c%E|r zP*OcT&k~!tE<aEP*EGn~q;nmrwo;5eV=ab}i39ZK-zIUyw=46IX-019RNH=X$w=uI zBJjH@ZJ;hk{!F1uZ}rx0qwTzaEBeps`933TYb_^@9yFW3J`~>7rns$0KfciZcn%a1 z;6s}PaO+wl8S&_TIM*%6%zGX>Izv8}r3<l7G^wIRiX4e7i+Vkp?-lq6O_ivrP#;la zgwu%G3fOvv;zg!~e@!+ncO>cr5-Luq?ykC2W*001P8ykeH>n5e_DdX0CVj8q7TbCy zsZCX=Hpv;6BQ90QS}1xj*HX9A@}s<Qr7?K_ye*PzAi0Lx!tod87e~-m-q1uU)PMBm zgK2wXfV+s@*kC}_rK4fGAo_rfuKiuVL2M1@-NFG$tW(p4+!xZUEm{@@9$TcWDha`$ zBS6h$nu&&=<hK}DLqzn1r3SCfe&kipPSNF7@!Rc?owiWQLG{8f^Vmn-B63DZfGdOp z?_Zr0+7nsYeFqLlcT~0FYA&-3<vd)4yXvdMNOjfuu}IzDq&>YUz4XoA-nYy^M3>)e zNsM36RHezUE~8pY6p0|Ogs}3re&XF^cCbF-4)GZP<Cky~MAwSy3VO1|Uvy#Hw;=K4 zL3L?SE7gv4j!D@~dio<Tu-D9Gr3;!`{0J%G_`SsiL@X--Y802E>%?XuL+p{v>?@kO zYWhaNUujL@2s>3_QmcpYPcDz55YoNF)n?Mkfu?K&BwR)*#;_J%OP6@`ud3gyUe*CG z&4Ja;cAE!ro3!*0-pQvZ-);5H=abKiIsapP8&-q|rsJV@K_p`PH3T)Yv+PLD&g%)# zDZ9)!X?<+6P4Z0_#Y+%wp)oP#l?9g%&H6l}!R-KtY6dy<3=A_n!jEGg)g5FaSCKi2 zCMRP3n!t`1NCK!wXxy|os4_SRJ-)wJ6;lq61$0|_XT2uP=9Nl}Yy`+)Q&Kt#QJ8u0 zgXhy8?f8y*OJoQoYBCEI3bMtk*$Va55cv>3=(oGHm#{64=S!K`2_GBmiD02ivxHw$ z6HoKaSnPkJnkKzd9~2(7vw5K0mk&7681^2%1F0txuyGxu@&`J2Y<eM|E3nmDuj$NI zyE*=1DFF?B8DF924)BoLQ_ClZtIpyd%CWU^<6$NO<ynrbBYRrOeYFXAsNT|VhJPW9 zC(1jBtZcJ0bIBR|E!H!U{@LK|-V4Qm4<wm;Pk@cr54zqQqhH<_mumHD7paeG1eg|% zsfi-pqFKk*;$E=oaCNllEZ+t_V!rX+)1x>e89ZpPH1ePs<kBRND?_`)qps8aB=K!@ zFRLrjPei=Rigv5|fivBvI0w`GgWF@6E=L)^AoVP@joQJNS>ad?jxC9P{4oA!s?6I& zWCD))U0HkOrZV%NEJDTH<JIRjJO4@3I2-c1&D~blu;p_X8?$WIjDRO<c2~$&+k?>u zu_tFNaq}B2b99iBS*rR?moO(dJ~eR{aeua|d)QAe5-Td2no4YM0FFpySYcs|8B7`k zCCQkucE5jwp3HSPq-?$q8WODitQg_P*)yy&lG73W4!k@MeQl0ykyG;~jD%d9-hl(( zw|4fIaS>!}GHzQol$&%0Q`ZnNT20^<Qjt&&KBrh&Rz2zHi15`j4n)|&auHHP4PVl) zmTs3u(Z`>sW+oe_KPvMu!evjMYC<61U}J44M_`=>$J_s+W~ivHFS&+iH{hb+YLr`9 z+^yzs%3VU>HYkbJ{LFj-t{JFEc{bYmxsI-;2qIi)9cWoYb!9P=bMIX4(tQ-f2^9OT z;ypF^<lCZCTr7mL@a~w+^(D#W9)0+yElBCfk7hRcAWkSp!pCF36ORwiC6?h#zVT?t zeLKG#adJ8{hlj2CIs>$+L~&5w1U#l`D9RyNs>I<w#TbGrDGCT-a@$^1sVhmlRnQEk zEuu^<1*2;xmeo$_o%s^sD$};r=KcvKhMtmU`LNN_@0B+Sb!qfIs%uK#3-hJ=s3RPG zj=hZh1i!5_tTe+fv<XD)xqHX^ihj%TWo13$T65)u9Y_8yqCY!pqXRrW7WtccEO+^` z_3wBjvp)18OYUDGJ~+bq%UF-XoZ<!_peM@@%S6Xc>@tW1h}-xrnNPAlT91liB;Z-( zTN-bF9&{Q)nMRshF^~>O1;nLyyRFhQXmH<Do|j=%?(r=8itlFeEh(7`h4+m)XV|?^ z<-1K!qYLh%sRc>Ns>cmzMsR#(orZ-ovX1YVrP%-+&V#aAMem51uC3OEDM)clWZWRW zrD3d~pcqyZ$>w3<%X%^~-myQz78G$=?ChQ=Mwtud5@j~1ws>5tvTHQh#=4q#G6pHN zmjjS**_Lg^h-m<rn1_a&S1O9Hi7WSaoG+u0q8*<R=F?snqg^xIth_!3c!M<F=%QeZ z?y0H`FNjXu#MvzaofdsNS(0xq@6A_Ui~{$d7t$aoh?D&B?vz=VrHXljO1jJZby3?> z;j!}y&g}NGi2WVnNzcu|EtBJo9OmRyOwnJ%#eKzXKXWk^|6{t{ImLhV(D1lJ5KtH8 znskN9pjtaP^ENXeC!VgS@`z9~S3qEPCPav097kZ<rnN>Nh0CpQd1Wt1VvGDalV(`Z z`U+tT>mwHPLbLJEJ5euhhqK{h5&te)pL>U2bnI5iQA4_q^5QalgFcW5!sF-Y7@zvq z7t6{>IeiC<W4Xy-ju#&`h&r1n9beqCptT0A@@ISi;O{G6%w_jvsoi`Xc!x*@HoP1@ z_(YBKi~*McJ0hI9rTizcSJlJ9nCdNrSElvBvR~}_<uN!%l`<H~k$~cU4eKQREb(N8 z#@n%jbi}v@K_N=Cm1{>^tNOFmk8eXC48YD$eD}olB4^iLtg;z8%_^sYZMRt!Z(6a5 z+8nU_=5(8FkC06AbAl7lbchVihx~AgiB_HFGAn1r?@}*clsrlK`1z?oba-M<Bd+A} zV?)tn(sEZE-M^n5`|5$N2JTO9b^w@!$9~KDnzuJkK0+&qD=sT=Ni!ZkgI;)M3K<V) z2ccBwvSun~hfh77iv_w+InY#IxQ>U^fA64haPW@Y2%K!|j@UvVn?yk{odJWrXtnuR zvGIgZ7<wl`j5or&TIOe}O{mq{bB(S{gz+S5h2DKH_%dC6=7KXI6qd@*tNZ5FGT=Sw zJ(2FPaVCVe4|hqFS`C^N_nNS7mAx}t4(F$QZx1=WhXJK|OWzg+v*s>0wfiKge~oqi zRH>8rPV(Kb(22>ZvF@R}mQ8Hy^7+0-J%U0RKj+P7pq=Y^F|<C*{+skKOdc_b)1YZ7 zGx0u6xoNgIH2rW_gV_p3F5&N9m8V{#U~~D4+i_lBbwbar!xLdt-JYk(3nz{jE=<#$ zmRM?(v0AiyKJ1QDLQ@E={8iBnK*@VS<>n3>|Gi&1chb<n%B_gc4&9O)-cv4wPbAeP z^XNLYgZt2Iy!KZKPxk(WU*;y8&=>pD4=E6-DMPqw8~tB=Ww^|Y`2_CJ7~c-EIn@_* zZ6<Bpa$=dPp`Lv;$2yPoM2JK%`RvR<R^N)Jei}>&{)WC!@F#rPf1mNan{szXogP_A zYPJwK@I1+Lok*4JBH7Y}DS@udlP2?rP>|f49p&_+;OmUjFQ+SQje^sJ3rZN#Nj<M9 z<$^A#?|6@%h=%2bXMWu?m`{WHCi<TIO7R3Vk1h}pTR3-;IU%BxPZhrKN#uhWdj_i* zJACN=S3`<}r#Sh<=43=DpN;UqWenBT^EtSGUHSwJ*xUPi<lOKMGF#5Qq-X;x`mbH4 zEbWTz$*O#j$R`9bKCi&roO0!98`j5r6gX|4m%FbfIOwK|T^^+MIKGrV;6eug(6ipY z5m)O74=Fk*N|o#!SR?FE6x;D3A8|^;JoNbKWRs5Ca@Iohq#CPXyKs9*+C~NnNu`CP zeICYU_803A8xR8;36st-KV0M;UtMVr!l5vu$MXu~BpFNRGuh;-k-ICF1VhV?3OrpH zKi*biHv%OkeWz^0ZIP`=JMu)aNM9XzZY|RFE~YsyKd?Fxj~c5;u`SkB=&x8>tg`GI znrI+C9X^hVX)4i9+Dsu1e(xVxp(=kpDl6shK2DaGUCMC6{>e@pl*-@bg$eKOUpGVm z5vrk_qq<-sSo{g{cleMEH*+IyYe_PZj6L^jF4Uo4ax51$0=F@+LcMI26waUM-2EKm z>69h@$rkDAkwh0t4<#z+;-1%Tl$7?kd>~99_?KGnEi?AX?oml=+GOjhsJmkuRTnGQ zIUuR#b)azuK(TfQZba4Y^t#KFbs6a<@BB0hc`9{nPrmo)J6;AD)y!i@goll+9Y+4~ zyTWWjMo445`SLUs49i>0+-Jr~z23HLwZM#ka|#O{%AGdOb~JtlPAkVukgfU(kBXFI z$+qgPbcrW^<!B8;<!+&P90K&NtS#(%BF%OgtgrTtn$W=_zzV^W&)5YV&Kz^L!L`5% z*9l)ULYwoXb2m5Oo*UsvjMIo^e-0(ogW~bJ_t37ey?{3FlUQd{*wM2b>ZhA~)*C4* zSB2#z_h+lN+rHCJ$abh~$>o-_^RrN2Ann`Cd@!VL$Z+UMs||Xq@GDQ0=}K#59wNiO zU6j0ECm-NtX!H#YZk6z=b?ZG;WZM>D<56TMl^hl1c|oINe?pEa^byO?5%v-;q<u2C ze;#uz02qUI)P{Ot`!{X_&4_!`wgrAg1mkp=m#PCbOOFMs*zy^sf;ZSJpC<7NINO^0 zG$$8TldOEQ)~DE$?5|QVLsi7V-`o!EPog+HlfZszRC2>6v6m+k-U>qVQUwUd6!#ZK zGVFD!o<keHVs>cOvi|vH*{xxXgntW$!l(@oXEv_Kh|f!L)i;WW@&)zlF(qDxTwpd- zomd|SE{`;+`X}EHgbF_vW{@2w)xxE1w43o+iXEjkM4d@Xnw81Ld{jWL38K`d!KD42 zXsWm0)xD6v)r`GXE;ywl5YtFa6L-fBC@j$_FyIhf=Piw?vlyr;qdMz3dG-NsZPsrC zJM#d=Ot>n`9rI3F_rpis0m;s4OGn)&MB<f?LI-b=oK+50B)5dZR(4Zyb({~m!@z7d zP0b=7uV%d$#@~T-kZHVtrE4pSxJ02yqo?<Af{g*_O4=P9D}yPRH?G@)XPF_RJ>(sS zAA-@fTA?M_qD)#i=hAMr=ORkiw_tQrq`#%WHh}==K8gP(Ryv2I=wni<;ol!TMZga# zPV#XBIdao8T|UrXubj-BN4;A7WvVy1GEsD9D7tZ=iPnu8O*)9Wjl7RC{ZADxZ}I96 zn}7wJ#xZ~VWP(${0B_ub)cvd8pD;ypIuQI*wCnh$K@qJ^Q$O)>@>f^tXTZ=G;DhfK zQ0OFwe=H4qXD1^yu_Eszp||`d>yA8-syfi*qpUDH)~~YNtAI-$wlhSf%)NNHwApg~ zQ=Jo-u-Uc|uz}cvxs3>uRqxkEl>Rhf2(z*doCmoCG^nsB5kl6vGI%Qi=mZ$a6sL-R zYrt(SsF!2hb(%xQWclZo`ExkMiJyIE1OXbw<C*K4{~c2EbX*>K$ADE0A}Imz4R$`Y zu^E($nLbK-XPZfy^>2L&-IDJGi<XhR@xmWt5F%^;D)~uZI`Exo*)$1E0||jxDv?gQ zOy%0~l~icR$su@`w#=0KYh=E%WUQ~PCvEtKPPu)&|9xeoj&u@N<O5GH%|9hAWfCQ- zq*EexEY3qN-vi1|vtmN6f6MfcOj0*C9MnPMETQo2mm1}NY1;$>opGisi0;^6vz=yu zWx8eIC7hzc#-mp^*8?8a(?T(rZ4!{jcJ?ujVU@zgEir&iz&EJ@=XWZjDL}A40Nr%> z+Zwl|j${k>{*hxcl6N)U3H-Ox4@ZILo-3!a&N<Q5!i_uTxQMmLg+R*(M*3YW0sp^M zO#j&B+E8mL$aaN(<ua<Yrrf6v)$1F`=cgt=^0UL6v6h0>2M%AaM}8&w!Q5^Avy#L= z@_Z_U`mzE~tH5Ds4@XnP##c`t#hTQ`)f{=Z&YZl4Zxbsu)kkXmY{+{1U5%KjpOyWx zYbjqZU#v1UEoQxx;~v<rQZ2W;=2+2@=^>Jy+R4n=tEAs3{TaWI4cso{ypXprAUEx2 zY6_MSt8)6#$CITD?iW93>l-NrTfHfForwsEybteG!7Ddnt8MG=(kB(7gt#%h<Q_;L zLN`DENuPFNvcYQRAbKQje0Df$X>-$hC_22&>C+h1W?Xr!txu*_>t!WIZE7VSxGg|t z$)fq(Ak*ClC>E%Wr<-)kkG&lQtnlEDGWm*Ig&Zg{Dn~Nm*LX3F46oA`Q?Gq@ex^ub zSQrPX+WsPjJ*s1lP@{tMpnW-imDeT6<(u!yz1tjh9O(mr^JVd+<){2-b<)99?>EUr zEE)CP=ty#mVshWInHcy=+F2bFj+{~?ximH5%X6la###d~iq7i?;1a5b>4vij9geiA zrNPdl9$YS36Nx$89;KP$6FZgql=9U(-*>(Q7oWVX!+9{Ox96`szO=VcACMe-F@u&a zr4qj_H8O4K?mu<=0yi&5<c1bPKllBUVroqZAQw(sBLc#SvR0x!%9lRb*pL`e$IF5k zzUaXkbinQ><UPfRx*xoQVEA@czI_2lsxoH-*=0viLV3uPgTn71inGo}>*lW={c6Uv zCl&{`5u1eQO?ThV4aU_I{@eLhgyzKMrCl1lKQZ?&;m=0lwo8%ijbS~?O_{sxl>uM} zekZ=1%@27pujPI0e-rEV4Z9w$%~=%KKsj<gPb1tnEfoC14g^6$#`~807%#rcX1|aB z@967@2%2z)QRwl-@rCW0Q_jRq9#(t7Cxt<g3ZU^yxG)|VC}B{JSvdv2r)qda{m+UK zroH=e>)?HTi>#Y*7<P!GRUNJ)RYIfH^VbK`5`Bkz#SDC9^^lLy6@@!S_k+qxOv7@T zJa&JCJoAny32(4q`<YUamQ>37;(z^?Zxf@VVph_eDgGHh6UR3o?nRv44J6q*(g|Of z(Ft_M!ZpO43suRBU5chs5Ii$iR86l4W4SpT_01^aAIiN{ZqRT(yy1z=N6CmjI?ae_ zYaZ60cwoL}y(YQS4Qv=*B$>tNFL25UbYMz^*J^_&0K9a0uSC&g4w?&Ve{}3)z@}gg zAP9ep0@`Q;z`wzRH+Qo`AhF?6a-UTO@s9%H!A9^-i*907CiIO)WM!LjE&uQDlj-@w zXnZc{a3b33PF(Ou`V@yaB?y}*1AgDYDrHO3uIYepTMH{1`*7%>%+K}RqnGWggW3Zg zS9xR>d(ZXD&ck<OffN@2ZY@lWK!1^oENsMXB-lqBq`#`{$k53hzCh;68?R-^Q9J2| zGWYT6#io4uOpR@29YX5RG|whWxl2`bg%iAY<Y_?#%%1AS^CShy1bBP0Rq|Cp%qap+ z+dCbA)LT=;*j_v}WEai^J|k9VJA`963aUu*Ab3%wRKO?(9#T`E%V#k(ifI6&n>z)2 zMWx0Wo#Frc`_z5|H|=-hzWM{Ab;1V4hPGlXf4B*4&J4(l%uA<7Xu@kUY5Kmp8L}}E qjqQ(4Oz2_u8F8tIa#FM7369-wV1nfN>p%eP@2!-=n^Fm*fd2!Fn(BZ6 literal 0 HcmV?d00001 From 4fa74d1bf8be1ae0178dd283127bff77e1a487c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:36:29 +0200 Subject: [PATCH 0412/1008] build(deps): bump aws.sdk.version from 2.20.116 to 2.20.118 (#1338) Bumps `aws.sdk.version` from 2.20.116 to 2.20.118. Updates `software.amazon.awssdk:bom` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:http-client-spi` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:url-connection-client` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:sqs` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:s3` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:dynamodb` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:lambda` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:cloudwatch` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:xray` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:cloudformation` from 2.20.116 to 2.20.118 Updates `software.amazon.awssdk:sts` from 2.20.116 to 2.20.118 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 20e1b4581..7b3b79a4a 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.116</aws.sdk.version> + <aws.sdk.version>2.20.118</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index e1a4b659e..80a0a7dfe 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.116</version> + <version>2.20.118</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 3e751f186..6a24501be 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ <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.116</aws.sdk.version> + <aws.sdk.version>2.20.118</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From fc3e9710c21f76bc8e938feb212f430874053350 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:49:08 +0200 Subject: [PATCH 0413/1008] build(deps): bump aws.sdk.version from 2.20.118 to 2.20.119 (#1343) Bumps `aws.sdk.version` from 2.20.118 to 2.20.119. Updates `software.amazon.awssdk:bom` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:http-client-spi` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:url-connection-client` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:sqs` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:s3` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:dynamodb` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:lambda` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:cloudwatch` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:xray` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:cloudformation` from 2.20.118 to 2.20.119 Updates `software.amazon.awssdk:sts` from 2.20.118 to 2.20.119 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 7b3b79a4a..35d5e340c 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.118</aws.sdk.version> + <aws.sdk.version>2.20.119</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 80a0a7dfe..aeffcac40 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.118</version> + <version>2.20.119</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 6a24501be..38e061dc7 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ <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.118</aws.sdk.version> + <aws.sdk.version>2.20.119</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From d0c7f912d66a499516f497710e52a64034dbecb6 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:43:56 +0100 Subject: [PATCH 0414/1008] feat: Add Batch Processor module (#1317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Starting to sketch out shape of API for batch processor * Variant 1 * Some more examples * Add extra bit for handling message-specific mutation * Make clear what's not public * test with interfaces * move tests * refactoring a bit * refactoring and adding FIFO * refactoring and adding FIFO * adding FIFO management * cleanup * add javadoc * Flesh out builder option a bit * Flesh out a bit more * more changes * Leaning into the builder style. needs some more thought * The shape of it is rightish * Working working * Work * Work on kinesis batch handler * More tests * More tests and starting to add an example * Working on batch * feat(batch): initial DdbBatchMessageHandler implementation * more * fix pom.xml for powertools-examples-batch * Add dynamodb example * Move template into subdir * Better structure * tidy up * Trying to get kinesis going * Kinesis demo working * Updated readme * Deprecated everywhere * Address initial review comments * Add success tests for Kinesis/S3 * Increase DDB coverage * Tell sonar to ignore dupes in examples * Add docs * Add warning * More doco * Format * Docs good * Disabling formatting check for now as its breaking the build and I can't work out how to autoapply it from intellij properly * Make checkstyle happy * Add docs from heitor * More docs changes * move ddb template in the right folder * Changes * add items updates and deletions to ddb example * Will it blend? * More changes * e2e test handler * Try work for SQS only * More greatness * Almost good * SQS works * Also kinesis e2e * Lets try doing it with streams * Try make it work with streams * Streams? * Make SQS test work * SQS and Kinesis work * DynamoDB E2E works * Formatting * Try exclude e2e-tests from dupe checking * Rename sonar file * Formatting * Update docs/utilities/batch.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update docs/utilities/batch.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Address review comments * Missed one * Formatting * Cleanup doc linking * More doco * Update docs/utilities/batch.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update batch.md Address review comments * Skip aspectj run --------- Co-authored-by: Scott Gerring <scott.gerring@spookfish.com> Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> Co-authored-by: Michele Ricciardi <mriccia@amazon.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- .sonarcloud.properties | 2 + docs/utilities/batch.md | 805 ++++++++++-------- docs/utilities/sqs_batch.md | 489 +++++++++++ docs/utilities/sqs_large_message_handling.md | 8 +- examples/README.md | 3 +- examples/pom.xml | 1 + examples/powertools-examples-batch/README.md | 35 + .../deploy/ddb-streams/template.yaml | 74 ++ .../deploy/kinesis/template.yml | 83 ++ .../deploy/sqs/template.yml | 147 ++++ examples/powertools-examples-batch/pom.xml | 198 +++++ .../dynamo/DynamoDBStreamBatchHandler.java | 32 + .../org/demo/batch/dynamo/DynamoDBWriter.java | 108 +++ .../batch/kinesis/KinesisBatchHandler.java | 33 + .../batch/kinesis/KinesisBatchSender.java | 78 ++ .../java/org/demo/batch/model/DdbProduct.java | 88 ++ .../java/org/demo/batch/model/Product.java | 84 ++ .../org/demo/batch/sqs/SqsBatchHandler.java | 33 + .../org/demo/batch/sqs/SqsBatchSender.java | 77 ++ .../src/main/resources/log4j2.xml | 16 + mkdocs.yml | 3 + pom.xml | 1 + powertools-batch/pom.xml | 68 ++ .../batch/BatchMessageHandlerBuilder.java | 58 ++ .../AbstractBatchMessageHandlerBuilder.java | 142 +++ .../DynamoDbBatchMessageHandlerBuilder.java | 55 ++ .../KinesisBatchMessageHandlerBuilder.java | 58 ++ .../SqsBatchMessageHandlerBuilder.java | 64 ++ .../DeserializationNotSupportedException.java | 28 + .../batch/handler/BatchMessageHandler.java | 38 + .../handler/DynamoDbBatchMessageHandler.java | 80 ++ .../KinesisStreamsBatchMessageHandler.java | 98 +++ .../batch/handler/SqsBatchMessageHandler.java | 124 +++ .../batch/DdbBatchProcessorTest.java | 127 +++ .../batch/KinesisBatchProcessorTest.java | 156 ++++ .../batch/SQSBatchProcessorTest.java | 171 ++++ .../lambda/powertools/batch/model/Basket.java | 67 ++ .../powertools/batch/model/Product.java | 84 ++ .../src/test/resources/dynamo_event.json | 97 +++ .../src/test/resources/kinesis_event.json | 38 + .../src/test/resources/sqs_event.json | 55 ++ .../src/test/resources/sqs_fifo_event.json | 58 ++ powertools-e2e-tests/handlers/batch/pom.xml | 72 ++ .../lambda/powertools/e2e/Function.java | 172 ++++ .../lambda/powertools/e2e/model/Product.java | 56 ++ .../batch/src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/pom.xml | 11 + powertools-e2e-tests/pom.xml | 7 +- .../amazon/lambda/powertools/BatchE2ET.java | 277 ++++++ .../powertools/testutils/Infrastructure.java | 74 +- .../utilities/EventDeserializer.java | 17 + .../lambda/powertools/sqs/SqsBatch.java | 5 + .../lambda/powertools/sqs/SqsUtils.java | 47 + 53 files changed, 4460 insertions(+), 358 deletions(-) create mode 100644 .sonarcloud.properties create mode 100644 docs/utilities/sqs_batch.md create mode 100644 examples/powertools-examples-batch/README.md create mode 100644 examples/powertools-examples-batch/deploy/ddb-streams/template.yaml create mode 100644 examples/powertools-examples-batch/deploy/kinesis/template.yml create mode 100644 examples/powertools-examples-batch/deploy/sqs/template.yml create mode 100644 examples/powertools-examples-batch/pom.xml create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandler.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBWriter.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/model/DdbProduct.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/model/Product.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java create mode 100644 examples/powertools-examples-batch/src/main/resources/log4j2.xml create mode 100644 powertools-batch/pom.xml create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/BatchMessageHandlerBuilder.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/AbstractBatchMessageHandlerBuilder.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/DynamoDbBatchMessageHandlerBuilder.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/KinesisBatchMessageHandlerBuilder.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/SqsBatchMessageHandlerBuilder.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/exception/DeserializationNotSupportedException.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/BatchMessageHandler.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java create mode 100644 powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java create mode 100644 powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/KinesisBatchProcessorTest.java create mode 100644 powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/SQSBatchProcessorTest.java create mode 100644 powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Basket.java create mode 100644 powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Product.java create mode 100644 powertools-batch/src/test/resources/dynamo_event.json create mode 100644 powertools-batch/src/test/resources/kinesis_event.json create mode 100644 powertools-batch/src/test/resources/sqs_event.json create mode 100644 powertools-batch/src/test/resources/sqs_fifo_event.json create mode 100644 powertools-e2e-tests/handlers/batch/pom.xml create mode 100644 powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/model/Product.java create mode 100644 powertools-e2e-tests/handlers/batch/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/BatchE2ET.java diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 000000000..1bd93ed9e --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1,2 @@ +# Ignore code duplicates in the examples +sonar.cpd.exclusions=examples/**/*,powertools-e2e-tests/**/* \ No newline at end of file diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 95704b8a0..6b38b438c 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -1,460 +1,571 @@ --- -title: SQS Batch Processing +title: Batch Processing description: Utility --- -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. +The batch processing utility provides a way to handle partial failures when processing batches of messages from SQS queues, +SQS FIFO queues, Kinesis Streams, or DynamoDB Streams. + +```mermaid +stateDiagram-v2 + direction LR + BatchSource: Amazon SQS <br/><br/> Amazon Kinesis Data Streams <br/><br/> Amazon DynamoDB Streams <br/><br/> + LambdaInit: Lambda invocation + BatchProcessor: Batch Processor + RecordHandler: Record Handler function + YourLogic: Your logic to process each batch item + LambdaResponse: Lambda response + BatchSource --> LambdaInit + LambdaInit --> BatchProcessor + BatchProcessor --> RecordHandler + state BatchProcessor { + [*] --> RecordHandler: Your function + RecordHandler --> YourLogic + } + RecordHandler --> BatchProcessor: Collect results + BatchProcessor --> LambdaResponse: Report items that failed processing +``` **Key Features** -* Prevent successfully processed messages from being returned to SQS -* A simple interface for individually processing messages from a batch +* Reports batch item failures to reduce number of retries for a record upon errors +* Simple interface to process each batch record +* Integrates with Java Events library and the deserialization module +* Build your own batch processor by extending primitives **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. +When using SQS, Kinesis Data Streams, or DynamoDB Streams as a Lambda event source, your Lambda functions are +triggered with a batch of messages. +If your function fails to process any message from the batch, the entire batch returns to your queue or stream. +This same batch is then retried until either condition happens first: +**a)** your Lambda function returns a successful response, +**b)** record reaches maximum retry attempts, or +**c)** records expire. + +```mermaid +journey + section Conditions + Successful response: 5: Success + Maximum retries: 3: Failure + Records expired: 1: Failure +``` + +This behavior changes when you enable Report Batch Item Failures feature in your Lambda function event source configuration: + +<!-- markdownlint-disable MD013 --> +* [**SQS queues**](#sqs-standard). Only messages reported as failure will return to the queue for a retry, while successful ones will be deleted. +* [**Kinesis data streams**](#kinesis-and-dynamodb-streams) and [**DynamoDB streams**](#kinesis-and-dynamodb-streams). +Single reported failure will use its sequence number as the stream checkpoint. +Multiple reported failures will use the lowest sequence number as checkpoint. + +With this utility, batch records are processed individually – only messages that failed to be processed +return to the queue or stream for a further retry. You simply build a `BatchProcessor` in your handler, +and return its response from the handler's `processMessage` implementation. Exceptions are handled +internally and an appropriate partial response for the message source is returned to Lambda for you. !!! 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. + While this utility lowers the chance of processing messages more than once, it is still not guaranteed. + We recommend implementing processing logic in an idempotent manner wherever possible, for instance, + by taking advantage of [the idempotency module](idempotency.md). 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. +We simply add `powertools-batch` to our build dependencies. Note - if you are using other Powertools +modules that require code-weaving, such as `powertools-core`, you will need to configure that also. -=== "Maven Java 11+" +=== "Maven" - ```xml hl_lines="3-7 16 18 24-27"" + ```xml <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> + <artifactId>powertools-batch</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" +=== "Gradle" - ```groovy hl_lines="3 11" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } + ```groovy repositories { mavenCentral() } dependencies { - aspect 'software.amazon.lambda:powertools-sqs:{{ powertools.version }}' + implementation 'software.amazon.lambda:powertools-batch:{{ powertools.version }}' } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 ``` +## Getting Started -## 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: +For this feature to work, you need to **(1)** configure your Lambda function event source to use `ReportBatchItemFailures`, +and **(2)** return a specific response to report which records failed to be processed. -* **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 +You can use your preferred deployment framework to set the correct configuration while this utility, +while the `powertools-batch` module handles generating the response, which simply needs to be returned as the result of +your Lambda handler. -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. +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. -## Functional Interface SqsMessageHandler +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). -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 +!!! note "You do not need any additional IAM permissions to use this utility, except for what each event source requires." -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. +### Processing messages from SQS -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" +=== "SQSBatchHandler" + + ```java hl_lines="10 13-15 20 25" + 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> { - ```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; + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } - public class AppSqsEvent implements RequestHandler<SQSEvent, String> { @Override - @SqsBatch(SampleMessageHandler.class) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); } - 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; - } + private void processMessage(Product p, Context c) { + // Process the product } + } ``` -=== "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; +=== "SQS Product" - 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}"; + ```java + public class Product { + private long id; + + private String name; + + private double price; + + public Product() { } - public class SampleMessageHandler implements SqsMessageHandler<Object> { + public Product(long id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } - @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; - } + public long getId() { + return id; } - } - ``` - - -### 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); + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } - return returnValues; + public void setName(String name) { + this.name = name; } - public class SampleMessageHandler implements SqsMessageHandler<String> { + public double getPrice() { + return price; + } - @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; - } + public void setPrice(double price) { + this.price = price; } } + ``` + +=== "SQS Example Event" + + ```json + { + "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": "e9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 12345,\n \"name\": \"product5\",\n \"price\": 45\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" + }] + } ``` -=== "Function implementation" +### Processing messages from Kinesis Streams - ```java hl_lines="5 6 7 8 9 10" - public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { +=== "KinesisBatchHandler" + + ```java hl_lines="10 13-15 20 24" + 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.batch.BatchMessageHandlerBuilder; + import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + + public class KinesisBatchHandler implements RequestHandler<KinesisEvent, StreamsEventResponse> { + + private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; + + public KinesisBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } @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; - }); + public StreamsEventResponse handleRequest(KinesisEvent kinesisEvent, Context context) { + return handler.processBatch(kinesisEvent, context); + } - return returnValues; + private void processMessage(Product p, Context c) { + // process the product } + } ``` -## 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()); +=== "Kinesis Product" + + ```java + public class Product { + private long id; + + private String name; + + private double price; + + public Product() { } - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); + public Product(long id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } + + public long getId() { + return id; + } - return returnValues; + public void setId(long id) { + this.id = id; } - public class SampleMessageHandler implements SqsMessageHandler<String> { + public String getName() { + return name; + } - @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; - } + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; } } + ``` + +=== "Kinesis Example Event" + + ```json + { + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "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": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "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" + } + ] + } ``` +### Processing messages from DynamoDB Streams -## 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" +=== "DynamoDBStreamBatchHandler" + + ```java hl_lines="10 13-15 20 24" + 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 software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; + import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + + public class DynamoDBStreamBatchHandler implements RequestHandler<DynamodbEvent, StreamsEventResponse> { + + private final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler; + + public DynamoDBStreamBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } + @Override - @SqsBatch(value = SampleMessageHandler.class, suppressException = true) - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; + public StreamsEventResponse handleRequest(DynamodbEvent ddbEvent, Context context) { + return handler.processBatch(ddbEvent, context); } - ``` -=== "Within SqsUtils Utility API" + private void processMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord, Context context) { + // Process the change record + } + } + ``` - ```java hl_lines="3" - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, true, SampleMessageHandler.class); - - return returnValues; +=== "DynamoDB Example Event" + + ```json + { + "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": "4421584500000000017450439091", + "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" + } + ] } ``` -## 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. +## Handling Messages -If you want such messages to be deleted instead, set `SqsBatch#deleteNonRetryableMessageFromQueue()` to `true`. By default, its value is `false`. +### Raw message and deserialized message handlers +You must provide either a raw message handler, or a deserialized message handler. The raw message handler receives +the envelope record type relevant for the particular event source - for instance, the SQS event source provides +[SQSMessage](https://javadoc.io/doc/com.amazonaws/aws-lambda-java-events/2.2.2/com/amazonaws/services/lambda/runtime/events/SQSEvent.html) +instances. The deserialized message handler extracts the body from this envelope, and deserializes it to a user-defined +type. Note that deserialized message handlers are not relevant for the DynamoDB provider, as the format of the inner +message is fixed by DynamoDB. -Same capability is also provided by [SqsUtils Utility API](#sqsutils-utility-api). +In general, the deserialized message handler should be used unless you need access to information on the envelope. -!!! info - Make sure the lambda function has required permissions needed by utility. Refer [this section](#iam-permissions). +=== "Raw Message Handler" -=== "SqsBatch annotation" + ```java + public void setup() { + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processRawMessage); + } - ```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> { + private void processRawMessage(SQSEvent.SQSMessage sqsMessage) { + // Do something with the raw message + } - @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()); - } +=== "Deserialized Message Handler" - return returnVal; - } - } + ```java + public void setup() { + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWitMessageHandler(this::processRawMessage, Product.class); + } + + private void processMessage(Product product) { + // Do something with the deserialized message } + ``` -=== "SqsBatch API" +### Success and failure handlers + +You can register a success or failure handler which will be invoked as each message is processed by the batch +module. This may be useful for reporting - for instance, writing metrics or logging failures. + +These handlers are optional. Batch failures are handled by the module regardless of whether or not you +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 +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); +``` - ```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()); +!!! info + If the success handler throws an exception, the item it is processing will be marked as failed by the + batch processor. + If the failure handler throws, the batch processing will continue; the item it is processing has + already been marked as failed. - if(/**Business validation failure**/) { - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); - } - return returnVal; - } +### Lambda Context + +Both raw and deserialized message handlers can choose to take the Lambda context as an argument if they +need it, or not: + +```java + public class ClassWithHandlers { + + private void processMessage(Product product) { + // Do something with the raw message + } + + private void processMessageWithContext(Product product, Context context) { + // Do something with the raw message and the lambda Context } } - ``` +``` diff --git a/docs/utilities/sqs_batch.md b/docs/utilities/sqs_batch.md new file mode 100644 index 000000000..658f7b085 --- /dev/null +++ b/docs/utilities/sqs_batch.md @@ -0,0 +1,489 @@ +--- +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 index 6308f1c79..0924d01cf 100644 --- a/docs/utilities/sqs_large_message_handling.md +++ b/docs/utilities/sqs_large_message_handling.md @@ -1,11 +1,13 @@ --- -title: SQS Large Message Handling +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) for the new module (`powertools-large-messages`) documentation. + 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. diff --git a/examples/README.md b/examples/README.md index b44ff2433..9b76faa82 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,9 +9,10 @@ Each example can be copied from its subdirectory and used independently of the r * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [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 +* [powertools-examples-sqs](powertools-examples-sqs) - Processes SQS batch requests (**Deprecated** - will be replaced by `powertools-examples-batch` in version 2 of this library) * [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 ## 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 diff --git a/examples/pom.xml b/examples/pom.xml index 72f1dc03b..5d19a20fb 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -34,6 +34,7 @@ <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> <module>powertools-examples-sqs</module> + <module>powertools-examples-batch</module> <module>powertools-examples-validation</module> <module>powertools-examples-cloudformation</module> </modules> diff --git a/examples/powertools-examples-batch/README.md b/examples/powertools-examples-batch/README.md new file mode 100644 index 000000000..d65fb584a --- /dev/null +++ b/examples/powertools-examples-batch/README.md @@ -0,0 +1,35 @@ + # Powertools for AWS Lambda (Java) - Batch Example + +This project contains examples of Lambda function using the batch processing 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/). + +Three different examples and SAM deployments are included, covering each of the batch sources: + +* [SQS](src/main/java/org/demo/batch/sqs) - SQS batch processing +* [Kinesis Streams](src/main/java/org/demo/batch/kinesis) - Kinesis Streams batch processing +* [DynamoDB Streams](src/main/java/org/demo/batch/dynamo) - DynamoDB Streams batch processing + +## 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) + +This sample contains three different deployments, depending on which batch processor you'd like to use, you can +change to the subdirectory containing the example SAM template, and deploy. For instance, for the SQS batch +deployment: + +```bash +cd deploy/sqs +sam build +sam deploy --guided +``` + +## Test the application + +Each of the examples uses a Lambda scheduled every 5 minutes to push a batch, and a separate lambda to read it. To +see this in action, we can simply tail the logs of our stack: + +```bash +sam logs --tail $STACK_NAME +``` \ No newline at end of file diff --git a/examples/powertools-examples-batch/deploy/ddb-streams/template.yaml b/examples/powertools-examples-batch/deploy/ddb-streams/template.yaml new file mode 100644 index 000000000..91f8799c4 --- /dev/null +++ b/examples/powertools-examples-batch/deploy/ddb-streams/template.yaml @@ -0,0 +1,74 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + DynamoDB Streams batch processing demo + +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active + Architectures: + - x86_64 + Environment: + Variables: + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 1.0 + POWERTOOLS_LOGGER_LOG_EVENT: true + +Resources: + DynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: id + AttributeType: S + KeySchema: + - AttributeName: id + KeyType: HASH + ProvisionedThroughput: + ReadCapacityUnits: 5 + WriteCapacityUnits: 5 + StreamSpecification: + StreamViewType: NEW_IMAGE + + + DemoDynamoDBWriter: + Type: AWS::Serverless::Function + Properties: + CodeUri: ../.. + Handler: org.demo.batch.dynamo.DynamoDBWriter::handleRequest + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: ddbstreams-demo + TABLE_NAME: !Ref DynamoDBTable + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref DynamoDBTable + Events: + CWSchedule: + Type: Schedule + Properties: + Schedule: 'rate(1 minute)' + Name: !Join [ "-", [ "ddb-writer-schedule", !Select [ 0, !Split [ -, !Select [ 2, !Split [ /, !Ref AWS::StackId ] ] ] ] ] ] + Description: Write records to DynamoDB via a Lambda function + Enabled: true + + DemoDynamoDBStreamsConsumerFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ../.. + Handler: org.demo.batch.dynamo.DynamoDBStreamBatchHandler::handleRequest + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: ddbstreams-batch-demo + Policies: AWSLambdaDynamoDBExecutionRole + Events: + Stream: + Type: DynamoDB + Properties: + Stream: !GetAtt DynamoDBTable.StreamArn + BatchSize: 100 + StartingPosition: TRIM_HORIZON + diff --git a/examples/powertools-examples-batch/deploy/kinesis/template.yml b/examples/powertools-examples-batch/deploy/kinesis/template.yml new file mode 100644 index 000000000..dcece61b8 --- /dev/null +++ b/examples/powertools-examples-batch/deploy/kinesis/template.yml @@ -0,0 +1,83 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + Kinesis batch processing demo + +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 1.0 + POWERTOOLS_LOGGER_LOG_EVENT: true + +Resources: + + DemoKinesisStream: + Type: AWS::Kinesis::Stream + Properties: + ShardCount: 1 + + StreamConsumer: + Type: "AWS::Kinesis::StreamConsumer" + Properties: + StreamARN: !GetAtt DemoKinesisStream.Arn + ConsumerName: KinesisBatchHandlerConsumer + + DemoKinesisSenderFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ../.. + Handler: org.demo.batch.kinesis.KinesisBatchSender::handleRequest + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: kinesis-batch-demo + STREAM_NAME: !Ref DemoKinesisStream + Policies: + - Statement: + - Sid: WriteToKinesis + Effect: Allow + Action: + - kinesis:PutRecords + - kinesis:DescribeStream + Resource: !GetAtt DemoKinesisStream.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 Kinesis via a Lambda function + Enabled: true + + DemoKinesisConsumerFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ../.. + Handler: org.demo.batch.kinesis.KinesisBatchHandler::handleRequest + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: kinesis-demo + Events: + Kinesis: + Type: Kinesis + Properties: + Stream: !GetAtt StreamConsumer.ConsumerARN + StartingPosition: LATEST + BatchSize: 2 + +Outputs: + DemoKinesisQueue: + Description: "ARN for Kinesis Stream" + Value: !GetAtt DemoKinesisStream.Arn + DemoKinesisSenderFunction: + Description: "Kinesis Batch Sender - Lambda Function ARN" + Value: !GetAtt DemoKinesisSenderFunction.Arn + DemoSQSConsumerFunction: + Description: "SQS Batch Handler - Lambda Function ARN" + Value: !GetAtt DemoKinesisConsumerFunction.Arn + diff --git a/examples/powertools-examples-batch/deploy/sqs/template.yml b/examples/powertools-examples-batch/deploy/sqs/template.yml new file mode 100644 index 000000000..764ba4863 --- /dev/null +++ b/examples/powertools-examples-batch/deploy/sqs/template.yml @@ -0,0 +1,147 @@ +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_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 1.0 + 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/powertools-batch-sqs-demo + 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.batch.sqs.SqsBatchSender::handleRequest + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: sqs-batch-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.batch.sqs.SqsBatchHandler::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: "SQS Batch Sender - Lambda Function ARN" + Value: !GetAtt DemoSQSSenderFunction.Arn + DemoSQSConsumerFunction: + Description: "SQS Batch Handler - 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-batch/pom.xml b/examples/powertools-examples-batch/pom.xml new file mode 100644 index 000000000..d3c4bc49b --- /dev/null +++ b/examples/powertools-examples-batch/pom.xml @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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> + + <groupId>software.amazon.lambda.examples</groupId> + <version>1.17.0-SNAPSHOT</version> + <artifactId>powertools-examples-batch</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) library 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> + </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-batch</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>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <version>${sdk.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sqs</artifactId> + <version>${sdk.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>url-connection-client</artifactId> + <version>${sdk.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>dynamodb-enhanced</artifactId> + <version>${sdk.version}</version> + </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>kinesis</artifactId> + <version>${sdk.version}</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> + </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> + </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 new file mode 100644 index 000000000..988c49e86 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandler.java @@ -0,0 +1,32 @@ +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.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +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 final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler; + + public DynamoDBStreamBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } + + @Override + public StreamsEventResponse handleRequest(DynamodbEvent ddbEvent, Context context) { + return handler.processBatch(ddbEvent, context); + } + + private void processMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord, Context context) { + 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 new file mode 100644 index 000000000..953ba8f23 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBWriter.java @@ -0,0 +1,108 @@ +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.ScheduledEvent; +import java.security.SecureRandom; +import java.util.List; +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.demo.batch.model.DdbProduct; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.enhanced.dynamodb.model.BatchWriteItemEnhancedRequest; +import software.amazon.awssdk.enhanced.dynamodb.model.BatchWriteResult; +import software.amazon.awssdk.enhanced.dynamodb.model.WriteBatch; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +public class DynamoDBWriter implements RequestHandler<ScheduledEvent, String> { + + private static final Logger LOGGER = LogManager.getLogger(DynamoDBWriter.class); + + private final DynamoDbEnhancedClient enhancedClient; + + private final SecureRandom random; + + public DynamoDBWriter() { + random = new SecureRandom(); + DynamoDbClient dynamoDbClient = DynamoDbClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .build(); + + enhancedClient = DynamoDbEnhancedClient.builder() + .dynamoDbClient(dynamoDbClient) + .build(); + } + + @Override + public String handleRequest(ScheduledEvent scheduledEvent, Context context) { + String tableName = System.getenv("TABLE_NAME"); + + LOGGER.info("handleRequest"); + + List<DdbProduct> products = createProducts(tableName); + List<DdbProduct> updatedProducts = updateProducts(tableName, products); + deleteProducts(tableName, updatedProducts); + + return "Success"; + } + + private void deleteProducts(String tableName, List<DdbProduct> updatedProducts) { + WriteBatch.Builder<DdbProduct> productDeleteBuilder = WriteBatch.builder(DdbProduct.class) + .mappedTableResource(enhancedClient.table(tableName, TableSchema.fromBean(DdbProduct.class))); + + updatedProducts.forEach(productDeleteBuilder::addDeleteItem); + + BatchWriteResult batchDeleteResult = enhancedClient + .batchWriteItem(BatchWriteItemEnhancedRequest.builder().writeBatches( + productDeleteBuilder.build()) + .build()); + LOGGER.info("Deleted batch of objects from DynamoDB: {}", batchDeleteResult); + } + + private List<DdbProduct> updateProducts(String tableName, List<DdbProduct> products) { + WriteBatch.Builder<DdbProduct> productUpdateBuilder = WriteBatch.builder(DdbProduct.class) + .mappedTableResource(enhancedClient.table(tableName, TableSchema.fromBean(DdbProduct.class))); + + List<DdbProduct> updatedProducts = products.stream().map(product -> { + // Update the price of the product and add it to the batch + LOGGER.info("Updating product: {}", product); + float price = random.nextFloat(); + DdbProduct updatedProduct = new DdbProduct(product.getId(), "updated-product-" + product.getId(), price); + productUpdateBuilder.addPutItem(updatedProduct); + return updatedProduct; + }).collect(Collectors.toList()); + + BatchWriteResult batchUpdateResult = enhancedClient + .batchWriteItem(BatchWriteItemEnhancedRequest.builder().writeBatches( + productUpdateBuilder.build()) + .build()); + LOGGER.info("Updated batch of objects to DynamoDB: {}", batchUpdateResult); + return updatedProducts; + } + + public List<DdbProduct> createProducts(String tableName) { + WriteBatch.Builder<DdbProduct> productBuilder = WriteBatch.builder(DdbProduct.class) + .mappedTableResource(enhancedClient.table(tableName, TableSchema.fromBean(DdbProduct.class))); + + List<DdbProduct> ddbProductStream = IntStream.range(0, 5).mapToObj(i -> { + String id = UUID.randomUUID().toString(); + float price = random.nextFloat(); + // Create a new product and add it to the batch + final DdbProduct product = new DdbProduct(id, "product-" + id, price); + productBuilder.addPutItem(product); + return product; + }).collect(Collectors.toList()); + + BatchWriteResult batchWriteResult = enhancedClient + .batchWriteItem(BatchWriteItemEnhancedRequest.builder().writeBatches( + productBuilder.build()) + .build()); + LOGGER.info("Wrote batch of objects to DynamoDB: {}", batchWriteResult); + return ddbProductStream; + } +} 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 new file mode 100644 index 000000000..d9339549b --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java @@ -0,0 +1,33 @@ +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.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +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 final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; + + public KinesisBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } + + @Override + public StreamsEventResponse handleRequest(KinesisEvent kinesisEvent, Context context) { + return handler.processBatch(kinesisEvent, 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/kinesis/KinesisBatchSender.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java new file mode 100644 index 000000000..0bc7dc42c --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchSender.java @@ -0,0 +1,78 @@ +package org.demo.batch.kinesis; + +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.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +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 software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +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; + + +/** + * A Lambda handler used to send message batches to Kinesis Streams. This is only here + * to produce an end-to-end demo, so that the {{@link org.demo.batch.kinesis.KinesisBatchHandler}} + * has some data to consume. + */ +public class KinesisBatchSender implements RequestHandler<ScheduledEvent, String> { + + private static final Logger LOGGER = LogManager.getLogger(KinesisBatchSender.class); + + private final KinesisClient kinesisClient; + private final SecureRandom random; + private final ObjectMapper objectMapper; + + public KinesisBatchSender() { + kinesisClient = KinesisClient.builder() + .httpClient(UrlConnectionHttpClient.create()) + .build(); + random = new SecureRandom(); + objectMapper = new ObjectMapper(); + } + + @Override + public String handleRequest(ScheduledEvent scheduledEvent, Context context) { + String streamName = System.getenv("STREAM_NAME"); + + LOGGER.info("handleRequest"); + + // Push 5 messages on each invoke. + List<PutRecordsRequestEntry> records = IntStream.range(0, 5) + .mapToObj(value -> { + long id = random.nextLong(); + float price = random.nextFloat(); + Product product = new Product(id, "product-" + id, price); + try { + SdkBytes data = SdkBytes.fromUtf8String(objectMapper.writeValueAsString(product)); + return PutRecordsRequestEntry.builder() + .partitionKey(String.format("%d", id)) + .data(data) + .build(); + } catch (JsonProcessingException e) { + LOGGER.error("Failed serializing body", e); + throw new RuntimeException(e); + } + }).collect(toList()); + + PutRecordsResponse putRecordsResponse = kinesisClient.putRecords(PutRecordsRequest.builder() + .streamName(streamName) + .records(records) + .build()); + + LOGGER.info("Sent Message {}", putRecordsResponse); + + return "Success"; + } +} diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/model/DdbProduct.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/model/DdbProduct.java new file mode 100644 index 000000000..9d69eac5d --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/model/DdbProduct.java @@ -0,0 +1,88 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.model; + +import java.util.Objects; +import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; +import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; + +@DynamoDbBean +public class DdbProduct { + private String id; + + private String name; + + private double price; + + public DdbProduct() { + } + + public DdbProduct(String id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } + + @DynamoDbPartitionKey + public String getId() { + return id; + } + + public void setId(String 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; + } + DdbProduct that = (DdbProduct) o; + return Double.compare(that.price, price) == 0 && Objects.equals(id, that.id) && Objects.equals(name, that.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/examples/powertools-examples-batch/src/main/java/org/demo/batch/model/Product.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/model/Product.java new file mode 100644 index 000000000..64da1804e --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/model/Product.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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/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 new file mode 100644 index 000000000..bb9d704d3 --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java @@ -0,0 +1,33 @@ +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.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.demo.batch.model.Product; +import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + +public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + private final static Logger LOGGER = LogManager.getLogger(SqsBatchHandler.class); + 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.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/SqsBatchSender.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java new file mode 100644 index 000000000..af78bed5a --- /dev/null +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java @@ -0,0 +1,77 @@ +package org.demo.batch.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.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +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 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; + + +/** + * A Lambda handler used to send message batches to SQS. This is only here + * to produce an end-to-end demo, so that the {{@link org.demo.batch.sqs.SqsBatchHandler}} + * has some data to consume. + */ +public class SqsBatchSender implements RequestHandler<ScheduledEvent, String> { + + private static final Logger LOGGER = LogManager.getLogger(SqsBatchSender.class); + + private final SqsClient sqsClient; + private final SecureRandom random; + private final ObjectMapper objectMapper; + + public SqsBatchSender() { + sqsClient = SqsClient.builder() + .httpClient(UrlConnectionHttpClient.create()) + .build(); + random = new SecureRandom(); + objectMapper = new ObjectMapper(); + } + + @Override + 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) + .mapToObj(value -> { + long id = random.nextLong(); + float price = random.nextFloat(); + Product product = new Product(id, "product-" + id, price); + try { + + return SendMessageBatchRequestEntry.builder() + .id(scheduledEvent.getId() + value) + .messageBody(objectMapper.writeValueAsString(product)) + .build(); + } catch (JsonProcessingException e) { + LOGGER.error("Failed serializing body", e); + throw new RuntimeException(e); + } + }).collect(toList()); + + SendMessageBatchResponse sendMessageBatchResponse = sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() + .queueUrl(queueUrl) + .entries(batchRequestEntries) + .build()); + + LOGGER.info("Sent Message {}", sendMessageBatchResponse); + + return "Success"; + } +} diff --git a/examples/powertools-examples-batch/src/main/resources/log4j2.xml b/examples/powertools-examples-batch/src/main/resources/log4j2.xml new file mode 100644 index 000000000..ea3ecf474 --- /dev/null +++ b/examples/powertools-examples-batch/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> \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 62d8d75ce..d54ece508 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,6 +18,9 @@ nav: - 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 diff --git a/pom.xml b/pom.xml index 38e061dc7..8c9e540f5 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ <module>powertools-idempotency</module> <module>powertools-large-messages</module> <module>powertools-e2e-tests</module> + <module>powertools-batch</module> <module>examples</module> </modules> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml new file mode 100644 index 000000000..9e25dabd8 --- /dev/null +++ b/powertools-batch/pom.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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-parent</artifactId> + <version>1.17.0-SNAPSHOT</version> + </parent> + + <build> + <plugins> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <artifactId>powertools-batch</artifactId> + <dependencies> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> + <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.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-tests</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> + </dependencies> + +</project> \ No newline at end of file diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/BatchMessageHandlerBuilder.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/BatchMessageHandlerBuilder.java new file mode 100644 index 000000000..4ed44453b --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/BatchMessageHandlerBuilder.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.batch; + +import software.amazon.lambda.powertools.batch.builder.DynamoDbBatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.builder.KinesisBatchMessageHandlerBuilder; +import software.amazon.lambda.powertools.batch.builder.SqsBatchMessageHandlerBuilder; + +/** + * A builder-style interface we can use to build batch processing handlers for SQS, Kinesis Streams, + * and DynamoDB Streams batches. The batch processing handlers that are returned allow + * the user to easily process batches of messages, one-by-one, while offloading + * the common issues - failure handling, partial responses, deserialization - + * to the library. + * + * @see <a href="https://docs.powertools.aws.dev/lambda/java/utilities/batch/">Powertools for AWS Lambda (Java) Batch Documentation</a> + **/ +public class BatchMessageHandlerBuilder { + + /** + * Build an SQS-batch message handler. + * + * @return A fluent builder interface to continue the building + */ + public SqsBatchMessageHandlerBuilder withSqsBatchHandler() { + return new SqsBatchMessageHandlerBuilder(); + } + + /** + * Build a DynamoDB streams batch message handler. + * + * @return A fluent builder interface to continue the building + */ + public DynamoDbBatchMessageHandlerBuilder withDynamoDbBatchHandler() { + return new DynamoDbBatchMessageHandlerBuilder(); + } + + /** + * Builds a Kinesis streams batch message handler. + * + * @return a fluent builder interface to continue the building + */ + public KinesisBatchMessageHandlerBuilder withKinesisBatchHandler() { + return new KinesisBatchMessageHandlerBuilder(); + } +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/AbstractBatchMessageHandlerBuilder.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/AbstractBatchMessageHandlerBuilder.java new file mode 100644 index 000000000..9b0647770 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/AbstractBatchMessageHandlerBuilder.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.batch.builder; + +import com.amazonaws.services.lambda.runtime.Context; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + +/** + * An abstract class to capture common arguments used across all the message-binding-specific batch processing + * builders. The builders provide a fluent interface to configure the batch processors. Any arguments specific + * to a particular batch binding can be added to the child builder. + * <p> + * We capture types for the various messages involved, so that we can provide an interface that makes + * sense for the concrete child. + * + * @param <T> The type of a single message in the batch + * @param <C> The type of the child builder. We need this to provide a fluent interface - see also getThis() + * @param <E> The type of the Lambda batch event + * @param <R> The type of the batch response we return to Lambda + */ +abstract class AbstractBatchMessageHandlerBuilder<T, C, E, R> { + protected BiConsumer<T, Throwable> failureHandler; + protected Consumer<T> successHandler; + + /** + * Provides an (Optional!) success handler. A success handler is invoked + * once for each message after it has been processed by the user-provided + * handler. + * <p> + * If the success handler throws, the item in the batch will be + * marked failed. + * + * @param handler The handler to invoke + */ + public C withSuccessHandler(Consumer<T> handler) { + this.successHandler = handler; + return getThis(); + } + + /** + * Provides an (Optional!) failure handler. A failure handler is invoked + * once for each message after it has failed to be processed by the + * user-provided handler. This gives the user's code a useful hook to do + * anything else that might have to be done in response to a failure - for + * instance, updating a metric, or writing a detailed log. + * <p> + * Please note that this method has nothing to do with the partial batch + * failure mechanism. Regardless of whether a failure handler is + * specified, partial batch failures and responses to the Lambda environment + * are handled by the batch utility separately. + * + * @param handler The handler to invoke on failure + */ + public C withFailureHandler(BiConsumer<T, Throwable> handler) { + this.failureHandler = handler; + return getThis(); + } + + /** + * Builds a BatchMessageHandler that can be used to process batches, given + * a user-defined handler to process each item in the batch. This variant + * takes a function that consumes a raw message and the Lambda context. This + * is useful for handlers that need access to the entire message object, not + * just the deserialized contents of the body. + * <p> + * Note: If you don't need the Lambda context, use the variant of this function + * that does not require it. + * + * @param handler Takes a raw message - the underlying AWS Events Library event - to process. + * For instance for SQS this would be an SQSMessage. + * @return A BatchMessageHandler for processing the batch + */ + public abstract BatchMessageHandler<E, R> buildWithRawMessageHandler(BiConsumer<T, Context> handler); + + /** + * Builds a BatchMessageHandler that can be used to process batches, given + * a user-defined handler to process each item in the batch. This variant + * takes a function that consumes a raw message and the Lambda context. This + * is useful for handlers that need access to the entire message object, not + * just the deserialized contents of the body. + * + * @param handler Takes a raw message - the underlying AWS Events Library event - to process. + * For instance for SQS this would be an SQSMessage. + * @return A BatchMessageHandler for processing the batch + */ + public BatchMessageHandler<E, R> buildWithRawMessageHandler(Consumer<T> handler) { + return buildWithRawMessageHandler((f, c) -> handler.accept(f)); + } + + /** + * Builds a BatchMessageHandler that can be used to process batches, given + * a user-defined handler to process each item in the batch. This variant + * takes a function that consumes the deserialized body of the given message + * and the lambda context. If deserialization fails, it will be treated as + * failure of the processing of that item in the batch. + * Note: If you don't need the Lambda context, use the variant of this function + * that does not require it. + * + * @param handler Processes the deserialized body of the message + * @return A BatchMessageHandler for processing the batch + */ + public abstract <M> BatchMessageHandler<E, R> buildWithMessageHandler(BiConsumer<M, Context> handler, + Class<M> messageClass); + + /** + * Builds a BatchMessageHandler that can be used to process batches, given + * a user-defined handler to process each item in the batch. This variant + * takes a function that consumes the deserialized body of the given message + * If deserialization fails, it will be treated as + * failure of the processing of that item in the batch. + * Note: If you don't need the Lambda context, use the variant of this function + * that does not require it. + * + * @param handler Processes the deserialized body of the message + * @return A BatchMessageHandler for processing the batch + */ + public <M> BatchMessageHandler<E, R> buildWithMessageHandler(Consumer<M> handler, Class<M> messageClass) { + return buildWithMessageHandler((f, c) -> handler.accept(f), messageClass); + } + + + /** + * Used to chain the fluent builder interface through the child classes. + * + * @return This + */ + protected abstract C getThis(); +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/DynamoDbBatchMessageHandlerBuilder.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/DynamoDbBatchMessageHandlerBuilder.java new file mode 100644 index 000000000..8513322b3 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/DynamoDbBatchMessageHandlerBuilder.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.batch.builder; + +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.function.BiConsumer; +import software.amazon.lambda.powertools.batch.exception.DeserializationNotSupportedException; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; +import software.amazon.lambda.powertools.batch.handler.DynamoDbBatchMessageHandler; + +/** + * Builds a batch processor for processing DynamoDB Streams batch events + **/ +public class DynamoDbBatchMessageHandlerBuilder + extends AbstractBatchMessageHandlerBuilder<DynamodbEvent.DynamodbStreamRecord, + DynamoDbBatchMessageHandlerBuilder, + DynamodbEvent, + StreamsEventResponse> { + + + @Override + public BatchMessageHandler<DynamodbEvent, StreamsEventResponse> buildWithRawMessageHandler( + BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> rawMessageHandler) { + return new DynamoDbBatchMessageHandler( + this.successHandler, + this.failureHandler, + rawMessageHandler); + } + + @Override + public <M> BatchMessageHandler<DynamodbEvent, StreamsEventResponse> buildWithMessageHandler( + BiConsumer<M, Context> handler, Class<M> messageClass) { + // The DDB provider streams DynamoDB changes, and therefore does not have a customizable payload + throw new DeserializationNotSupportedException(); + } + + @Override + protected DynamoDbBatchMessageHandlerBuilder getThis() { + return this; + } +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/KinesisBatchMessageHandlerBuilder.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/KinesisBatchMessageHandlerBuilder.java new file mode 100644 index 000000000..30bfcab65 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/KinesisBatchMessageHandlerBuilder.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.batch.builder; + +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.function.BiConsumer; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; +import software.amazon.lambda.powertools.batch.handler.KinesisStreamsBatchMessageHandler; + +/** + * Builds a batch processor for processing Kinesis Streams batch events + */ +public class KinesisBatchMessageHandlerBuilder + extends AbstractBatchMessageHandlerBuilder<KinesisEvent.KinesisEventRecord, + KinesisBatchMessageHandlerBuilder, + KinesisEvent, + StreamsEventResponse> { + @Override + public BatchMessageHandler<KinesisEvent, StreamsEventResponse> buildWithRawMessageHandler( + BiConsumer<KinesisEvent.KinesisEventRecord, Context> rawMessageHandler) { + return new KinesisStreamsBatchMessageHandler<Void>( + rawMessageHandler, + null, + null, + successHandler, + failureHandler); + } + + @Override + public <M> BatchMessageHandler<KinesisEvent, StreamsEventResponse> buildWithMessageHandler( + BiConsumer<M, Context> messageHandler, Class<M> messageClass) { + return new KinesisStreamsBatchMessageHandler<>( + null, + messageHandler, + messageClass, + successHandler, + failureHandler); + } + + @Override + protected KinesisBatchMessageHandlerBuilder getThis() { + return this; + } +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/SqsBatchMessageHandlerBuilder.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/SqsBatchMessageHandlerBuilder.java new file mode 100644 index 000000000..ee2dc23f6 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/builder/SqsBatchMessageHandlerBuilder.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.batch.builder; + +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.function.BiConsumer; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; +import software.amazon.lambda.powertools.batch.handler.SqsBatchMessageHandler; + +/** + * Builds a batch processor for the SQS event source. + */ +public class SqsBatchMessageHandlerBuilder extends AbstractBatchMessageHandlerBuilder<SQSEvent.SQSMessage, + SqsBatchMessageHandlerBuilder, + SQSEvent, + SQSBatchResponse> { + + + @Override + public BatchMessageHandler<SQSEvent, SQSBatchResponse> buildWithRawMessageHandler( + BiConsumer<SQSEvent.SQSMessage, Context> rawMessageHandler) { + return new SqsBatchMessageHandler<Void>( + null, + null, + rawMessageHandler, + successHandler, + failureHandler + ); + } + + @Override + public <M> BatchMessageHandler<SQSEvent, SQSBatchResponse> buildWithMessageHandler( + BiConsumer<M, Context> messageHandler, Class<M> messageClass) { + return new SqsBatchMessageHandler<>( + messageHandler, + messageClass, + null, + successHandler, + failureHandler + ); + } + + + @Override + protected SqsBatchMessageHandlerBuilder getThis() { + return this; + } + + +} diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/exception/DeserializationNotSupportedException.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/exception/DeserializationNotSupportedException.java new file mode 100644 index 000000000..6f3206c99 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/exception/DeserializationNotSupportedException.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.batch.exception; + +/** + * Thrown by message handlers that do not support deserializing arbitrary payload + * contents. This is the case for instance with DynamoDB Streams, which stream + * changesets about user-defined data, but not the user-defined data models themselves. + */ +public class DeserializationNotSupportedException extends RuntimeException { + + public DeserializationNotSupportedException() { + super("This BatchMessageHandler has a fixed schema and does not support user-defined types"); + } + +} 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 new file mode 100644 index 000000000..730211feb --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/BatchMessageHandler.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.batch.handler; + +import com.amazonaws.services.lambda.runtime.Context; + +/** + * The basic interface a batch message handler must meet. + * + * @param <E> The type of the Lambda batch event + * @param <R> The type of the lambda batch response + */ +public interface BatchMessageHandler<E, R> { + + /** + * Processes the given batch returning a partial batch + * response indicating the success and failure of individual + * messages within the batch. + * + * @param event The Lambda event containing the batch to process + * @param context The lambda context + * @return A partial batch response + */ + public abstract R processBatch(E event, Context context); + +} 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 new file mode 100644 index 000000000..aa6eba839 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.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.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.function.BiConsumer; +import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 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 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) { + this.successHandler = successHandler; + this.failureHandler = failureHandler; + this.rawMessageHandler = rawMessageHandler; + } + + @Override + public StreamsEventResponse processBatch(DynamodbEvent event, Context context) { + List<StreamsEventResponse.BatchItemFailure> batchFailures = new ArrayList<>(); + + for (DynamodbEvent.DynamodbStreamRecord record : event.getRecords()) { + try { + + 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); + } + } + } + } + + 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 new file mode 100644 index 000000000..fe1aaf354 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java @@ -0,0 +1,98 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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.function.BiConsumer; +import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.utilities.EventDeserializer; + +/** + * A batch message processor for Kinesis Streams batch processing. + * <p> + * Refer to <a href="https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-batchfailurereporting">Kinesis Batch failure reporting</a> + * + * @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 final BiConsumer<KinesisEvent.KinesisEventRecord, Context> rawMessageHandler; + private final BiConsumer<M, Context> messageHandler; + private final Class<M> messageClass; + private final Consumer<KinesisEvent.KinesisEventRecord> successHandler; + 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) { + + this.rawMessageHandler = rawMessageHandler; + this.messageHandler = messageHandler; + this.messageClass = messageClass; + this.successHandler = successHandler; + this.failureHandler = failureHandler; + } + + @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); + } + + // 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 new StreamsEventResponse(batchFailures); + } +} + 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 new file mode 100644 index 000000000..b3c416a69 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java @@ -0,0 +1,124 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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.function.BiConsumer; +import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.utilities.EventDeserializer; + +/** + * A batch message processor for SQS batches. + * + * @param <M> The user-defined type of the message payload + * @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); + + // 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 final Class<M> messageClass; + private final BiConsumer<M, Context> messageHandler; + private final BiConsumer<SQSEvent.SQSMessage, Context> rawMessageHandler; + private final Consumer<SQSEvent.SQSMessage> successHandler; + 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) { + this.messageHandler = messageHandler; + this.messageClass = messageClass; + this.rawMessageHandler = rawMessageHandler; + this.successHandler = successHandler; + this.failureHandler = failureHandler; + } + + @Override + public SQSBatchResponse processBatch(SQSEvent event, Context context) { + SQSBatchResponse response = SQSBatchResponse.builder().withBatchItemFailures(new ArrayList<>()).build(); + + // 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; + + int messageCursor = 0; + for (; messageCursor < event.getRecords().size() && !failWholeBatch; 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); + } + + } 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()); + if (messageGroupId != null) { + failWholeBatch = 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); + } + } + + } + } + + if (failWholeBatch) { + // Add the remaining messages to the batch item failures + event.getRecords() + .subList(messageCursor, event.getRecords().size()) + .forEach(message -> response.getBatchItemFailures() + .add(SQSBatchResponse.BatchItemFailure.builder().withItemIdentifier(message.getMessageId()) + .build())); + } + return response; + } +} 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 new file mode 100644 index 000000000..9e2c211e2 --- /dev/null +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.amazonaws.services.lambda.runtime.Context; +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.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.params.ParameterizedTest; +import org.mockito.Mock; +import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; + +public class DdbBatchProcessorTest { + + @Mock + private Context context; + + private void processMessageSucceeds(DynamodbEvent.DynamodbStreamRecord record, Context context) { + // Great success + } + + private void processMessageFailsForFixedMessage(DynamodbEvent.DynamodbStreamRecord record, Context context) { + if (record.getDynamodb().getSequenceNumber().equals("4421584500000000017450439091")) { + throw new RuntimeException("fake exception"); + } + } + + @ParameterizedTest + @Event(value = "dynamo_event.json", type = DynamodbEvent.class) + public void batchProcessingSucceedsAndReturns(DynamodbEvent event) { + // Arrange + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessageSucceeds); + + // Act + StreamsEventResponse dynamodbBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(0); + } + + @ParameterizedTest + @Event(value = "dynamo_event.json", type = DynamodbEvent.class) + public void shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent event) { + // Arrange + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessageFailsForFixedMessage); + + // Act + StreamsEventResponse dynamodbBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); + } + + @ParameterizedTest + @Event(value = "dynamo_event.json", type = DynamodbEvent.class) + public void failingFailureHandlerShouldntFailBatch(DynamodbEvent event) { + // Arrange + AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .withFailureHandler((m, e) -> { + if (m.getDynamodb().getSequenceNumber().equals("4421584500000000017450439091")) { + wasCalledAndFailed.set(true); + throw new RuntimeException("Success handler throws"); + } + }) + .buildWithRawMessageHandler(this::processMessageFailsForFixedMessage); + + // Act + StreamsEventResponse dynamodbBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(dynamodbBatchResponse).isNotNull(); + assertThat(dynamodbBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(wasCalledAndFailed.get()).isTrue(); + StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); + } + + @ParameterizedTest + @Event(value = "dynamo_event.json", type = DynamodbEvent.class) + public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(DynamodbEvent event) { + // Arrange + AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .withSuccessHandler((e) -> { + if (e.getDynamodb().getSequenceNumber().equals("4421584500000000017450439091")) { + wasCalledAndFailed.set(true); + throw new RuntimeException("Success handler throws"); + } + }) + .buildWithRawMessageHandler(this::processMessageSucceeds); + + // Act + StreamsEventResponse dynamodbBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(dynamodbBatchResponse).isNotNull(); + assertThat(dynamodbBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(wasCalledAndFailed.get()).isTrue(); + StreamsEventResponse.BatchItemFailure batchItemFailure = dynamodbBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("4421584500000000017450439091"); + } + +} 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 new file mode 100644 index 000000000..d78638e1d --- /dev/null +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/KinesisBatchProcessorTest.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.batch; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.amazonaws.services.lambda.runtime.Context; +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.concurrent.atomic.AtomicBoolean; +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 { + + @Mock + private Context context; + + private void processMessageSucceeds(KinesisEvent.KinesisEventRecord record, Context context) { + // Great success + } + + private void processMessageFailsForFixedMessage(KinesisEvent.KinesisEventRecord record, Context context) { + if (record.getKinesis().getSequenceNumber() + .equals("49545115243490985018280067714973144582180062593244200961")) { + 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) { + if (product.getId() == 1234) { + throw new RuntimeException("fake exception"); + } + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + public void batchProcessingSucceedsAndReturns(KinesisEvent event) { + // Arrange + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithRawMessageHandler(this::processMessageSucceeds); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(0); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + public void shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { + // Arrange + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithRawMessageHandler(this::processMessageFailsForFixedMessage); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( + "49545115243490985018280067714973144582180062593244200961"); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + public void shouldAddMessageToBatchFailure_whenException_withProduct(KinesisEvent event) { + // Arrange + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithMessageHandler(this::processMessageFailsForFixedProduct, Product.class); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); + StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( + "49545115243490985018280067714973144582180062593244200961"); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + public void failingFailureHandlerShouldntFailBatch(KinesisEvent event) { + // Arrange + AtomicBoolean wasCalled = new AtomicBoolean(false); + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .withFailureHandler((e, ex) -> { + wasCalled.set(true); + throw new RuntimeException("Well, this doesn't look great"); + }) + .buildWithMessageHandler(this::processMessageFailsForFixedProduct, Product.class); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(kinesisBatchResponse).isNotNull(); + assertThat(kinesisBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(wasCalled.get()).isTrue(); + StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( + "49545115243490985018280067714973144582180062593244200961"); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(KinesisEvent event) { + // Arrange + AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .withSuccessHandler((e) -> { + if (e.getKinesis().getSequenceNumber() + .equals("49545115243490985018280067714973144582180062593244200961")) { + wasCalledAndFailed.set(true); + throw new RuntimeException("Success handler throws"); + } + }) + .buildWithRawMessageHandler(this::processMessageSucceeds); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(kinesisBatchResponse).isNotNull(); + assertThat(kinesisBatchResponse.getBatchItemFailures().size()).isEqualTo(1); + assertThat(wasCalledAndFailed.get()).isTrue(); + StreamsEventResponse.BatchItemFailure batchItemFailure = kinesisBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo( + "49545115243490985018280067714973144582180062593244200961"); + } + +} 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 new file mode 100644 index 000000000..2f9429fa3 --- /dev/null +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/SQSBatchProcessorTest.java @@ -0,0 +1,171 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.amazonaws.services.lambda.runtime.Context; +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.concurrent.atomic.AtomicBoolean; +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 { + @Mock + private Context context; + + // A handler that works + private void processMessageSucceeds(SQSEvent.SQSMessage sqsMessage) { + } + + // 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")) { + 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) { + if (product.getId() == 12345) { + throw new RuntimeException("fake exception"); + } + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void batchProcessingSucceedsAndReturns(SQSEvent event) { + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessageSucceeds); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(0); + } + + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessageFailsForFixedMessage); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(1); + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + } + + @ParameterizedTest + @Event(value = "sqs_fifo_event.json", type = SQSEvent.class) + public void shouldAddMessageToBatchFailure_whenException_withSQSFIFO(SQSEvent event) { + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessageFailsForFixedMessage); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(2); + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(1); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("f9144555-9a4f-4ec3-99a0-34ce359b4b54"); + } + + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void shouldAddMessageToBatchFailure_whenException_withProduct(SQSEvent event) { + + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessageFailsForFixedProduct, Product.class); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); + assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(1); + + // Assert + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void failingFailureHandlerShouldntFailBatch(SQSEvent event) { + // Arrange + AtomicBoolean wasCalled = new AtomicBoolean(false); + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .withFailureHandler((e, ex) -> { + wasCalled.set(true); + throw new RuntimeException("Well, this doesn't look great"); + }) + .buildWithMessageHandler(this::processMessageFailsForFixedProduct, Product.class); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(sqsBatchResponse).isNotNull(); + assertThat(wasCalled.get()).isTrue(); + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + public void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(SQSEvent event) { + // Arrange + AtomicBoolean wasCalledAndFailed = new AtomicBoolean(false); + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .withSuccessHandler((e) -> { + if (e.getMessageId().equals("e9144555-9a4f-4ec3-99a0-34ce359b4b54")) { + wasCalledAndFailed.set(true); + throw new RuntimeException("Success handler throws"); + } + }) + .buildWithRawMessageHandler(this::processMessageSucceeds); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatch(event, context); + + // Assert + assertThat(sqsBatchResponse).isNotNull(); + assertThat(wasCalledAndFailed.get()).isTrue(); + SQSBatchResponse.BatchItemFailure batchItemFailure = sqsBatchResponse.getBatchItemFailures().get(0); + assertThat(batchItemFailure.getItemIdentifier()).isEqualTo("e9144555-9a4f-4ec3-99a0-34ce359b4b54"); + } + + +} diff --git a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Basket.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Basket.java new file mode 100644 index 000000000..6009e79d6 --- /dev/null +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Basket.java @@ -0,0 +1,67 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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 String toString() { + return "Basket{" + + "products=" + products + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash(products); + } +} diff --git a/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Product.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Product.java new file mode 100644 index 000000000..2695578f9 --- /dev/null +++ b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/model/Product.java @@ -0,0 +1,84 @@ +package software.amazon.lambda.powertools.batch.model; + +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +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-batch/src/test/resources/dynamo_event.json b/powertools-batch/src/test/resources/dynamo_event.json new file mode 100644 index 000000000..f28ce0e6e --- /dev/null +++ b/powertools-batch/src/test/resources/dynamo_event.json @@ -0,0 +1,97 @@ +{ + "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": "4421584500000000017450439091", + "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" + } + ] +} \ No newline at end of file diff --git a/powertools-batch/src/test/resources/kinesis_event.json b/powertools-batch/src/test/resources/kinesis_event.json new file mode 100644 index 000000000..c9068da9b --- /dev/null +++ b/powertools-batch/src/test/resources/kinesis_event.json @@ -0,0 +1,38 @@ +{ + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=", + "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": "eyJpZCI6MTIzNDUsICJuYW1lIjoicHJvZHVjdDUiLCAicHJpY2UiOjQ1fQ==", + "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" + } + ] +} \ No newline at end of file diff --git a/powertools-batch/src/test/resources/sqs_event.json b/powertools-batch/src/test/resources/sqs_event.json new file mode 100644 index 000000000..7fdad096f --- /dev/null +++ b/powertools-batch/src/test/resources/sqs_event.json @@ -0,0 +1,55 @@ +{ + "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": "e9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 12345,\n \"name\": \"product5\",\n \"price\": 45\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": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 123456,\n \"name\": \"product6\",\n \"price\": 46\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-batch/src/test/resources/sqs_fifo_event.json b/powertools-batch/src/test/resources/sqs_fifo_event.json new file mode 100644 index 000000000..e5abb1e5a --- /dev/null +++ b/powertools-batch/src/test/resources/sqs_fifo_event.json @@ -0,0 +1,58 @@ +{ + "Records": [ + { + "messageId": "d9144555-9a4f-4ec3-99a0-34ce359b4b54", + "receiptHandle": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 1234,\n \"name\": \"product\",\n \"price\": 42\n}", + "attributes": { + "MessageGroupId": "groupA", + "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": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 12345,\n \"name\": \"product5\",\n \"price\": 45\n}", + "attributes": { + "MessageGroupId": "groupA", + "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": "13e7f7851d2eaa5c01f208ebadbf1e72==", + "body": "{\n \"id\": 123456,\n \"name\": \"product6\",\n \"price\": 46\n}", + "attributes": { + "MessageGroupId": "groupA", + "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-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml new file mode 100644 index 000000000..995121e2a --- /dev/null +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -0,0 +1,72 @@ +<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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-batch</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using Powertools for AWS Lambda (Java) batch</name> + + <dependencies> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-batch</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-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> + </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> +</project> 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 new file mode 100644 index 000000000..64f5a02c2 --- /dev/null +++ b/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,172 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; +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.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +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 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() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processProductMessage, Product.class); + + kinesisHandler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithMessageHandler(this::processProductMessage, Product.class); + + ddbHandler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processDdbMessage); + + this.ddbOutputTable = System.getenv("TABLE_FOR_ASYNC_TESTS"); + } + + private void processProductMessage(Product p, Context c) { + LOGGER.info("Processing product " + p); + + // TODO - write product details to output table + ddbClient = DynamoDbClient.builder() + .build(); + Map<String, AttributeValue> results = new HashMap<>(); + results.put("functionName", AttributeValue.builder() + .s(c.getFunctionName()) + .build()); + results.put("id", AttributeValue.builder() + .s(Long.toString(p.getId())) + .build()); + results.put("name", AttributeValue.builder() + .s(p.getName()) + .build()); + results.put("price", AttributeValue.builder() + .n(Double.toString(p.getPrice())) + .build()); + ddbClient.putItem(PutItemRequest.builder() + .tableName(ddbOutputTable) + .item(results) + .build()); + } + + private void processDdbMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord, Context context) { + LOGGER.info("Processing DynamoDB Stream Record" + dynamodbStreamRecord); + + ddbClient = DynamoDbClient.builder() + .build(); + + String id = dynamodbStreamRecord.getDynamodb().getKeys().get("id").getS(); + LOGGER.info("Incoming ID is " + id); + + Map<String, AttributeValue> results = new HashMap<>(); + results.put("functionName", AttributeValue.builder() + .s(context.getFunctionName()) + .build()); + results.put("id", AttributeValue.builder() + .s(id) + .build()); + + ddbClient.putItem(PutItemRequest.builder() + .tableName(ddbOutputTable) + .item(results) + .build()); + } + + public Object createResult(String input, Context context) { + + LOGGER.info(input); + + PojoSerializer<SQSEvent> serializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, this.getClass().getClassLoader()); + SQSEvent event = serializer.fromJson(input); + if (event.getRecords().get(0).getEventSource().equals("aws:sqs")) { + LOGGER.info("Running for SQS"); + LOGGER.info(event); + return sqsHandler.processBatch(event, context); + } + + PojoSerializer<KinesisEvent> kinesisSerializer = + LambdaEventSerializers.serializerFor(KinesisEvent.class, this.getClass().getClassLoader()); + KinesisEvent kinesisEvent = kinesisSerializer.fromJson(input); + if (kinesisEvent.getRecords().get(0).getEventSource().equals("aws:kinesis")) { + 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()); + LOGGER.info("Running for DynamoDB"); + DynamodbEvent ddbEvent = ddbSerializer.fromJson(input); + return ddbHandler.processBatch(ddbEvent, context); + } + + @Override + public Object handleRequest(InputStream inputStream, Context context) { + + String input = new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); + + return createResult(input, context); + } +} diff --git a/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/model/Product.java b/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/model/Product.java new file mode 100644 index 000000000..74bb5ff9f --- /dev/null +++ b/powertools-e2e-tests/handlers/batch/src/main/java/software/amazon/lambda/powertools/e2e/model/Product.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.e2e.model; + +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; + } +} diff --git a/powertools-e2e-tests/handlers/batch/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/batch/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/batch/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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 4dd8cbb45..6e82c7aec 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -16,6 +16,7 @@ <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> @@ -72,6 +73,11 @@ <artifactId>powertools-large-messages</artifactId> <version>${lambda.powertools.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-batch</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> @@ -82,6 +88,11 @@ <artifactId>aws-lambda-java-events</artifactId> <version>${lambda.java.events}</version> </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-serialization</artifactId> + <version>${lambda.java.serialization}</version> + </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 2c802edc3..f3194c163 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -57,7 +57,12 @@ <version>${aws.sdk.version}</version> <scope>test</scope> </dependency> - + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>kinesis</artifactId> + <version>${aws.sdk.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>cloudwatch</artifactId> 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 new file mode 100644 index 000000000..c5f74594d --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/BatchE2ET.java @@ -0,0 +1,277 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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 software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpClient; +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.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.ScanRequest; +import software.amazon.awssdk.services.dynamodb.model.ScanResponse; +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.Infrastructure; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +public 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; + + private static ObjectMapper objectMapper; + private static String outputTable; + private static DynamoDbClient ddbClient; + private static SqsClient sqsClient; + private static KinesisClient kinesisClient; + private static String ddbStreamsTestTable; + private final List<Product> testProducts; + + public BatchE2ET() { + testProducts = Arrays.asList( + new Product(1, "product1", 1.23), + new Product(2, "product2", 4.56), + new Product(3, "product3", 6.78) + ); + } + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + public static void setup() { + String random = UUID.randomUUID().toString().substring(0, 6); + String queueName = "batchqueue" + random; + kinesisStreamName = "batchstream" + random; + ddbStreamsTestTable = "ddbstreams" + random; + + objectMapper = JsonConfig.get().getObjectMapper(); + + infrastructure = Infrastructure.builder() + .testName(BatchE2ET.class.getSimpleName()) + .pathToFunction("batch") + .queue(queueName) + .ddbStreamsTableName(ddbStreamsTestTable) + .kinesisStream(kinesisStreamName) + .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"); + ddbStreamsTestTable = outputs.get("DdbStreamsTestTable"); + + ddbClient = DynamoDbClient.builder() + .region(region) + .httpClient(httpClient) + .build(); + + // GIVEN + sqsClient = SqsClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + kinesisClient = KinesisClient.builder() + .httpClient(httpClient) + .region(region) + .build(); + } + + @AfterAll + public static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + + @AfterEach + public 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>() { + { + put("functionName", AttributeValue.builder() + .s(item.get("functionName").s()) + .build()); + put("id", AttributeValue.builder() + .s(item.get("id").s()) + .build()); + } + }; + + ddbClient.deleteItem(DeleteItemRequest.builder() + .tableName(outputTable) + .key(key) + .build()); + } + } + + @Test + public void sqsBatchProcessingSucceeds() throws InterruptedException { + List<SendMessageBatchRequestEntry> entries = testProducts.stream() + .map(p -> { + try { + return SendMessageBatchRequestEntry.builder() + .id(p.getName()) + .messageBody(objectMapper.writeValueAsString(p)) + .build(); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()); + + // WHEN + sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() + .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); + } + + @Test + public void kinesisBatchProcessingSucceeds() throws InterruptedException { + List<PutRecordsRequestEntry> entries = testProducts.stream() + .map(p -> { + try { + return PutRecordsRequestEntry.builder() + .partitionKey("1") + .data(SdkBytes.fromUtf8String(objectMapper.writeValueAsString(p))) + .build(); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()); + + // WHEN + PutRecordsResponse result = 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); + } + + @Test + public void ddbStreamsBatchProcessingSucceeds() throws InterruptedException { + // GIVEN + String theId = "my-test-id"; + + // WHEN + ddbClient.putItem(PutItemRequest.builder() + .tableName(ddbStreamsTestTable) + .item(new HashMap<String, AttributeValue>() { + { + put("id", AttributeValue.builder() + .s(theId) + .build()); + } + }) + .build()); + Thread.sleep(90000); // wait for function to be executed + + // THEN + ScanResponse items = ddbClient.scan(ScanRequest.builder() + .tableName(outputTable) + .build()); + + assertThat(items.count()).isEqualTo(1); + assertThat(items.items().get(0).get("id").s()).isEqualTo(theId); + } + + private void validateAllItemsHandled(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; + } + } + assertThat(foundIt).isTrue(); + } + } + + 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 String getName() { + return name; + } + + public double getPrice() { + return price; + } + } +} 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 996f49bd4..2a1af093c 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 @@ -45,14 +45,16 @@ 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.Attribute; -import software.amazon.awscdk.services.dynamodb.AttributeType; -import software.amazon.awscdk.services.dynamodb.BillingMode; -import software.amazon.awscdk.services.dynamodb.Table; +import software.amazon.awscdk.services.dynamodb.*; 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.StartingPosition; import software.amazon.awscdk.services.lambda.Tracing; +import software.amazon.awscdk.services.lambda.eventsources.DynamoEventSource; +import software.amazon.awscdk.services.lambda.eventsources.KinesisEventSource; import software.amazon.awscdk.services.lambda.eventsources.SqsEventSource; import software.amazon.awscdk.services.logs.LogGroup; import software.amazon.awscdk.services.logs.RetentionDays; @@ -110,8 +112,9 @@ public class Infrastructure { private final AppConfig appConfig; private final SdkHttpClient httpClient; private final String queue; + private final String kinesisStream; private final String largeMessagesBucket; - + private String ddbStreamsTableName; private String functionName; private Object cfnTemplate; private String cfnAssetDirectory; @@ -126,7 +129,9 @@ private Infrastructure(Builder builder) { this.idempotencyTable = builder.idemPotencyTable; this.appConfig = builder.appConfig; this.queue = builder.queue; + this.kinesisStream = builder.kinesisStream; this.largeMessagesBucket = builder.largeMessagesBucket; + this.ddbStreamsTableName = builder.ddbStreamsTableName; this.app = new App(); this.stack = createStackWithLambda(); @@ -279,7 +284,12 @@ private Stack createStackWithLambda() { .maxReceiveCount(1) // do not retry in case of error .build(); sqsQueue.grantConsumeMessages(function); - SqsEventSource sqsEventSource = SqsEventSource.Builder.create(sqsQueue).enabled(true).batchSize(1).build(); + SqsEventSource sqsEventSource = SqsEventSource.Builder + .create(sqsQueue) + .enabled(true) + .reportBatchItemFailures(true) + .batchSize(1) + .build(); function.addEventSource(sqsEventSource); CfnOutput.Builder .create(stack, "QueueURL") @@ -287,6 +297,46 @@ private Stack createStackWithLambda() { .build(); createTableForAsyncTests = true; } + if (!StringUtils.isEmpty(kinesisStream)) { + Stream stream = Stream.Builder + .create(stack, "KinesisStream") + .streamMode(StreamMode.ON_DEMAND) + .streamName(kinesisStream) + .build(); + + stream.grantRead(function); + KinesisEventSource kinesisEventSource = KinesisEventSource.Builder + .create(stream) + .enabled(true) + .batchSize(3) + .reportBatchItemFailures(true) + .startingPosition(StartingPosition.TRIM_HORIZON) + .maxBatchingWindow(Duration.seconds(1)) + .build(); + function.addEventSource(kinesisEventSource); + CfnOutput.Builder + .create(stack, "KinesisStreamName") + .value(stream.getStreamName()) + .build(); + } + + if (!StringUtils.isEmpty(ddbStreamsTableName)) { + Table ddbStreamsTable = Table.Builder.create(stack, "DDBStreamsTable") + .tableName(ddbStreamsTableName) + .stream(StreamViewType.KEYS_ONLY) + .removalPolicy(RemovalPolicy.DESTROY) + .partitionKey(Attribute.builder().name("id").type(AttributeType.STRING).build()) + .build(); + + DynamoEventSource ddbEventSource = DynamoEventSource.Builder.create(ddbStreamsTable) + .batchSize(1) + .startingPosition(StartingPosition.TRIM_HORIZON) + .maxBatchingWindow(Duration.seconds(1)) + .reportBatchItemFailures(true) + .build(); + function.addEventSource(ddbEventSource); + CfnOutput.Builder.create(stack, "DdbStreamsTestTable").value(ddbStreamsTable.getTableName()).build(); + } if (!StringUtils.isEmpty(largeMessagesBucket)) { Bucket offloadBucket = Bucket.Builder @@ -451,6 +501,8 @@ public static class Builder { private Map<String, String> environmentVariables = new HashMap<>(); private String idemPotencyTable; private String queue; + private String kinesisStream; + private String ddbStreamsTableName; private Builder() { getJavaRuntime(); @@ -526,6 +578,16 @@ public Builder queue(String queue) { return this; } + public Builder kinesisStream(String stream) { + this.kinesisStream = stream; + return this; + } + + public Builder ddbStreamsTableName(String tableName) { + this.ddbStreamsTableName = tableName; + return this; + } + public Builder largeMessagesBucket(String largeMessagesBucket) { this.largeMessagesBucket = largeMessagesBucket; return this; diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java index 22712e8ce..13ad4d28f 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java @@ -33,7 +33,9 @@ import com.amazonaws.services.lambda.runtime.events.SNSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; +import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectReader; import java.io.IOException; import java.util.List; @@ -96,6 +98,8 @@ public static EventPart extractDataFrom(Object object) { return new EventPart(event.getRecords().stream() .map(SQSEvent.SQSMessage::getBody) .collect(Collectors.toList())); + } else if (object instanceof SQSEvent.SQSMessage) { + return new EventPart(((SQSEvent.SQSMessage) object).getBody()); } else if (object instanceof ScheduledEvent) { ScheduledEvent event = (ScheduledEvent) object; return new EventPart(event.getDetail()); @@ -113,6 +117,8 @@ public static EventPart extractDataFrom(Object object) { return new EventPart(event.getRecords().stream() .map(r -> decode(r.getKinesis().getData())) .collect(Collectors.toList())); + } else if (object instanceof KinesisEvent.KinesisEventRecord) { + return new EventPart(decode(((KinesisEvent.KinesisEventRecord)object).getKinesis().getData())); } else if (object instanceof KinesisFirehoseEvent) { KinesisFirehoseEvent event = (KinesisFirehoseEvent) object; return new EventPart(event.getRecords().stream() @@ -214,6 +220,17 @@ public <T> T as(Class<T> clazz) { } } + public <M> M as() { + TypeReference<M> typeRef = new TypeReference<M>() {}; + + try { + JsonParser parser = JsonConfig.get().getObjectMapper().createParser(content); + return JsonConfig.get().getObjectMapper().reader().readValue(parser, typeRef); + } catch (IOException e) { + throw new EventDeserializationException("Cannot load the event as " + typeRef, e); + } + }; + /** * Deserialize this part of event from JSON to a list of objects of type T * 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 index d0ffe6a73..4378fa707 100644 --- 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 @@ -23,6 +23,10 @@ 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> @@ -87,6 +91,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) +@Deprecated public @interface SqsBatch { Class<? extends SqsMessageHandler<Object>> value(); 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 index c838180fd..1f00edf17 100644 --- 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 @@ -119,6 +119,11 @@ public static void overrideS3Client(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> @@ -146,12 +151,17 @@ public static void overrideS3Client(S3Client s3Client) { * @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> @@ -200,6 +210,7 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * @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) { @@ -207,6 +218,10 @@ public static <R> List<R> batchProcessor(final SQSEvent event, } /** + * @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> @@ -232,6 +247,7 @@ public static <R> List<R> batchProcessor(final SQSEvent 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) { @@ -241,6 +257,10 @@ public static <R> List<R> batchProcessor(final SQSEvent event, } /** + * @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> @@ -291,6 +311,7 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * @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, @@ -301,6 +322,10 @@ public static <R> List<R> batchProcessor(final SQSEvent event, } /** + * @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> @@ -355,6 +380,7 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * @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, @@ -367,6 +393,10 @@ public static <R> List<R> batchProcessor(final SQSEvent event, } /** + * @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> @@ -394,6 +424,7 @@ public static <R> List<R> batchProcessor(final SQSEvent 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); @@ -401,6 +432,10 @@ public static <R> List<R> batchProcessor(final SQSEvent event, /** + * @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> @@ -450,6 +485,7 @@ public static <R> List<R> batchProcessor(final SQSEvent event, * @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) { @@ -458,6 +494,10 @@ public static <R> List<R> batchProcessor(final SQSEvent event, /** + * @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> @@ -484,6 +524,7 @@ public static <R> List<R> batchProcessor(final SQSEvent 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) { @@ -491,7 +532,13 @@ public static <R> List<R> batchProcessor(final SQSEvent event, } + /** + * @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, From 889554f6af6bf48025952944ed1ccf7385cd5d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:33:40 +0200 Subject: [PATCH 0415/1008] chore: apply checkstyle again (#1339) * apply checkstyle again * exclude examples from sonar * exclude e2e handlers from sonar --- .sonarcloud.properties | 17 +- .../src/main/java/helloworld/App.java | 9 +- .../src/main/java/helloworld/App.java | 26 +- .../java/org/demo/sqs/SqsMessageSender.java | 32 +- .../CloudFormationResponseTest.java | 20 +- .../internal/UserAgentConfiguratorTest.java | 3 +- .../lambda/powertools/e2e/Function.java | 17 +- .../lambda/powertools/e2e/Function.java | 10 +- .../lambda/powertools/e2e/Function.java | 14 +- .../lambda/powertools/LargeMessageE2ET.java | 53 ++-- .../LargeMessageIdempotentE2ET.java | 3 +- .../powertools/testutils/Infrastructure.java | 57 ++-- .../testutils/metrics/MetricsFetcher.java | 60 ++-- .../testutils/tracing/TraceFetcher.java | 72 ++--- .../persistence/DynamoDBPersistenceStore.java | 32 +- .../largemessages/LargeMessage.java | 2 +- .../internal/LargeMessageAspect.java | 3 +- .../internal/LargeMessageProcessor.java | 3 +- .../internal/LargeSQSMessageProcessor.java | 70 ++--- .../internal/LargeMessageAspectTest.java | 40 ++- .../logging/internal/LambdaJsonLayout.java | 10 +- .../logging/internal/PowertoolsResolver.java | 12 +- .../core/layout/LambdaJsonLayoutTest.java | 44 +-- .../powertools/metrics/MetricsUtils.java | 18 +- .../powertools/metrics/MetricsLoggerTest.java | 96 +++--- ...MetricsEnabledDefaultDimensionHandler.java | 4 +- ...tricsEnabledDefaultNoDimensionHandler.java | 4 +- .../internal/LambdaMetricsAspectTest.java | 284 +++++++++--------- .../parameters/AppConfigProvider.java | 3 +- .../powertools/parameters/BaseProvider.java | 50 +-- .../parameters/DynamoDbProvider.java | 20 +- .../powertools/parameters/SSMProvider.java | 20 +- .../parameters/SecretsProvider.java | 4 +- .../parameters/DynamoDbProviderTest.java | 22 +- .../parameters/SSMProviderTest.java | 10 +- .../internal/LambdaParametersAspectTest.java | 6 +- .../utilities/EventDeserializer.java | 16 +- .../lambda/powertools/sqs/SqsUtils.java | 10 +- .../powertools/sqs/internal/BatchContext.java | 146 ++++----- .../sqs/internal/SqsLargeMessageAspect.java | 34 +-- .../sqs/SqsUtilsBatchProcessorTest.java | 190 ++++++------ .../sqs/SqsUtilsFifoBatchProcessorTest.java | 76 ++--- .../sqs/SqsUtilsLargeMessageTest.java | 54 ++-- .../internal/SqsLargeMessageAspectTest.java | 12 +- .../SqsMessageBatchProcessorAspectTest.java | 120 ++++---- .../testsuite/LoggingOrderTest.java | 12 +- .../powertools/tracing/TracingUtilsTest.java | 116 +++---- .../internal/LambdaTracingAspectTest.java | 242 +++++++-------- .../validation/ValidationUtilsTest.java | 36 +-- .../internal/ValidationAspectTest.java | 6 +- 50 files changed, 1139 insertions(+), 1081 deletions(-) diff --git a/.sonarcloud.properties b/.sonarcloud.properties index 1bd93ed9e..d9a4f79cf 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1,2 +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. +# +# +sonar.exclusions=examples/**/*,powertools-e2e-tests/handlers/**/* + # Ignore code duplicates in the examples -sonar.cpd.exclusions=examples/**/*,powertools-e2e-tests/**/* \ No newline at end of file +sonar.cpd.exclusions=examples/**/*,powertools-e2e-tests/**/* 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 ca3cb0ab7..54f13244c 100644 --- a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java @@ -2,6 +2,7 @@ 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 software.amazon.awssdk.awscore.exception.AwsServiceException; @@ -9,13 +10,15 @@ import software.amazon.awssdk.core.waiters.WaiterResponse; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.*; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.HeadBucketRequest; +import software.amazon.awssdk.services.s3.model.HeadBucketResponse; +import software.amazon.awssdk.services.s3.model.NoSuchBucketException; import software.amazon.awssdk.services.s3.waiters.S3Waiter; import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; import software.amazon.lambda.powertools.cloudformation.Response; -import java.util.Objects; - /** * Handler for requests to Lambda function. */ diff --git a/examples/powertools-examples-core/src/main/java/helloworld/App.java b/examples/powertools-examples-core/src/main/java/helloworld/App.java index 94360cf59..a1ea9a9e3 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core/src/main/java/helloworld/App.java @@ -61,10 +61,10 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); LoggingUtils.appendKey("test", "willBeLogged"); @@ -77,11 +77,11 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv 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); - }); + { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); threadOption1(); @@ -107,10 +107,10 @@ private void threadOption1() throws InterruptedException { private void threadOption2() throws InterruptedException { Entity traceEntity = AWSXRay.getTraceEntity(); Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> - { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); + { + String var = "somethingToProcess"; + log.info("inside threaded logging inline {}", var); + })); anotherThread.start(); anotherThread.join(); } 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 index 45856d198..701d6808f 100644 --- 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 @@ -63,22 +63,22 @@ public String handleRequest(final ScheduledEvent input, final Context context) { // 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()); + { + 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) 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 2e7fbcc0c..51f0e95f9 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 @@ -58,16 +58,16 @@ static CloudFormationResponse testableCloudFormationResponse() { ExecutableHttpRequest executableRequest = mock(ExecutableHttpRequest.class); when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> - { - HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); - assertThat(request.contentStreamProvider()).isPresent(); - - InputStream inputStream = request.contentStreamProvider().get().newStream(); - HttpExecuteResponse response = mock(HttpExecuteResponse.class); - when(response.responseBody()).thenReturn(Optional.of(AbortableInputStream.create(inputStream))); - when(executableRequest.call()).thenReturn(response); - return executableRequest; - }); + { + HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); + assertThat(request.contentStreamProvider()).isPresent(); + + InputStream inputStream = request.contentStreamProvider().get().newStream(); + HttpExecuteResponse response = mock(HttpExecuteResponse.class); + when(response.responseBody()).thenReturn(Optional.of(AbortableInputStream.create(inputStream))); + when(executableRequest.call()).thenReturn(response); + return executableRequest; + }); return new CloudFormationResponse(client); } 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 index 00110077f..2d75bdb3a 100644 --- 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 @@ -30,7 +30,8 @@ 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 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(); diff --git a/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 70f1bceea..36cb9fcd2 100644 --- a/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/largemessage/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -19,6 +19,9 @@ 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 java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -29,10 +32,6 @@ import software.amazon.lambda.powertools.largemessages.LargeMessage; import software.amazon.lambda.powertools.logging.Logging; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - public class Function implements RequestHandler<SQSEvent, SQSBatchResponse> { private static final String TABLE_FOR_ASYNC_TESTS = System.getenv("TABLE_FOR_ASYNC_TESTS"); @@ -49,7 +48,7 @@ public Function() { @Logging(logEvent = true) public SQSBatchResponse handleRequest(SQSEvent event, Context context) { - for (SQSMessage message: event.getRecords()) { + for (SQSMessage message : event.getRecords()) { processRawMessage(message, context); } return SQSBatchResponse.builder().build(); @@ -59,14 +58,18 @@ public SQSBatchResponse handleRequest(SQSEvent event, Context context) { 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)); + 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()); + 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()); } 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 b9f737857..5269b37c9 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 @@ -71,7 +71,7 @@ public Function(DynamoDbClient client) { @Logging(logEvent = true) public SQSBatchResponse handleRequest(SQSEvent event, Context context) { - for (SQSEvent.SQSMessage message: event.getRecords()) { + for (SQSEvent.SQSMessage message : event.getRecords()) { processRawMessage(message, context); } return SQSBatchResponse.builder().build(); @@ -82,7 +82,9 @@ public SQSBatchResponse handleRequest(SQSEvent event, Context context) { private String processRawMessage(@IdempotencyKey SQSEvent.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)); + throw new SecurityException( + String.format("message digest does not match, expected %s, got %s", sqsMessage.getMd5OfBody(), + bodyMD5)); } Instant now = Instant.now(); @@ -91,7 +93,9 @@ private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, item.put("id", AttributeValue.builder().s(sqsMessage.getMessageId()).build()); item.put("bodyMD5", AttributeValue.builder().s(bodyMD5).build()); item.put("now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()); - item.put("bodySize", AttributeValue.builder().n(String.valueOf(sqsMessage.getBody().getBytes(StandardCharsets.UTF_8).length)).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()); 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 462b7c71d..397e34a85 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 @@ -31,13 +31,13 @@ public String handleRequest(Input input, Context context) { String message = buildMessage(input.getMessage(), context.getFunctionName()); TracingUtils.withSubsegment("internal_stuff", subsegment -> - { - try { - Thread.sleep(100); // simulate stuff - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - }); + { + try { + Thread.sleep(100); // simulate stuff + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); return message; } 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 2d9f74135..548a710b8 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 @@ -1,7 +1,19 @@ 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.amazon.sqs.javamessaging.AmazonSQSExtendedClient; import com.amazon.sqs.javamessaging.ExtendedClientConfiguration; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +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; @@ -23,19 +35,6 @@ import software.amazon.awssdk.services.sqs.model.SendMessageRequest; import software.amazon.lambda.powertools.testutils.Infrastructure; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; - public class LargeMessageE2ET { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageE2ET.class); @@ -81,6 +80,13 @@ public static void setup() { LOG.info("Testing '" + LargeMessageE2ET.class.getSimpleName() + "'"); } + @AfterAll + public static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + @AfterEach public void reset() { if (messageId != null) { @@ -92,19 +98,14 @@ public void reset() { } } - @AfterAll - public static void tearDown() { - if (infrastructure != null) - infrastructure.destroy(); - } - @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); + 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); @@ -122,7 +123,8 @@ public void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException, In .builder() .tableName(tableName) .keyConditionExpression("functionName = :func") - .expressionAttributeValues(Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) + .expressionAttributeValues( + Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) .build(); QueryResponse response = dynamoDbClient.query(request); List<Map<String, AttributeValue>> items = response.items(); @@ -138,7 +140,8 @@ public void smallSQSMessage_shouldNotReadFromS3() throws IOException, Interrupte final ExtendedClientConfiguration extendedClientConfig = new ExtendedClientConfiguration() .withPayloadSupportEnabled(s3Client, bucketName); - AmazonSQSExtendedClient client = new AmazonSQSExtendedClient(SqsClient.builder().httpClient(httpClient).build(), extendedClientConfig); + AmazonSQSExtendedClient client = + new AmazonSQSExtendedClient(SqsClient.builder().httpClient(httpClient).build(), extendedClientConfig); String message = "Hello World"; // when @@ -155,13 +158,15 @@ public void smallSQSMessage_shouldNotReadFromS3() throws IOException, Interrupte .builder() .tableName(tableName) .keyConditionExpression("functionName = :func") - .expressionAttributeValues(Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) + .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(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 986d22a2f..e8ee3ca5c 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 @@ -142,7 +142,8 @@ public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throw .builder() .tableName(tableName) .keyConditionExpression("functionName = :func") - .expressionAttributeValues(Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) + .expressionAttributeValues( + Collections.singletonMap(":func", AttributeValue.builder().s(functionName).build())) .build(); QueryResponse response = dynamoDbClient.query(request); List<Map<String, AttributeValue>> items = response.items(); 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 2a1af093c..b1fab2883 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 @@ -177,7 +177,8 @@ public Map<String, String> deploy() { 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())); @@ -230,8 +231,8 @@ private Stack createStackWithLambda() { functionName = stackName + "-function"; CfnOutput.Builder.create(stack, FUNCTION_NAME_OUTPUT) - .value(functionName) - .build(); + .value(functionName) + .build(); LOG.debug("Building Lambda function with command " + packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); @@ -444,20 +445,20 @@ private void synthesize() { private void uploadAssets() { Map<String, Asset> assets = findAssets(); assets.forEach((objectKey, asset) -> - { - if (!asset.assetPath.endsWith(".jar")) { - return; - } - 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"); - return; - } - LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); - s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), - Paths.get(cfnAssetDirectory, asset.assetPath)); - }); + { + if (!asset.assetPath.endsWith(".jar")) { + return; + } + 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"); + 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,17 +473,17 @@ private Map<String, Asset> findAssets() { .readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); JsonNode files = jsonNode.get("files"); 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(); - Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account) - .replace("${AWS::Region}", region.toString())); - assets.put(objectKey, asset); - }); + { + 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(); + Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account) + .replace("${AWS::Region}", region.toString())); + assets.put(objectKey, asset); + }); } catch (IOException e) { throw new RuntimeException(e); } 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 349a9acc1..00728f451 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 @@ -73,33 +73,33 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String } Callable<List<Double>> callable = () -> - { - 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()) - .metricStat(MetricStat.builder() - .unit(StandardUnit.COUNT) - .metric(Metric.builder() - .namespace(namespace) - .metricName(metricName) - .dimensions(dimensionsList) - .build()) - .period(period) - .stat("Sum") - .build()) - .returnData(true) - .build()) - .build()); - List<Double> values = metricData.metricDataResults().get(0).values(); - if (values == null || values.isEmpty()) { - throw new Exception("No data found for metric " + metricName); - } - return values; - }; + { + 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()) + .metricStat(MetricStat.builder() + .unit(StandardUnit.COUNT) + .metric(Metric.builder() + .namespace(namespace) + .metricName(metricName) + .dimensions(dimensionsList) + .build()) + .period(period) + .stat("Sum") + .build()) + .returnData(true) + .build()) + .build()); + List<Double> values = metricData.metricDataResults().get(0).values(); + if (values == null || values.isEmpty()) { + throw new Exception("No data found for metric " + metricName); + } + return values; + }; RetryConfig retryConfig = new RetryConfigBuilder() .withMaxNumberOfTries(10) @@ -110,9 +110,9 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String CallExecutor<List<Double>> callExecutor = new CallExecutorBuilder<List<Double>>() .config(retryConfig) .afterFailedTryListener(s -> - { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); - }) + { + LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + }) .build(); Status<List<Double>> status = callExecutor.execute(callable); return status.getResult(); 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 dc63987fd..2b5b6e15b 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 @@ -89,10 +89,10 @@ public static Builder builder() { */ public Trace fetchTrace() { Callable<Trace> callable = () -> - { - List<String> traceIds = getTraceIds(); - return getTrace(traceIds); - }; + { + List<String> traceIds = getTraceIds(); + return getTrace(traceIds); + }; RetryConfig retryConfig = new RetryConfigBuilder() .withMaxNumberOfTries(10) @@ -103,9 +103,9 @@ public Trace fetchTrace() { CallExecutor<Trace> callExecutor = new CallExecutorBuilder<Trace>() .config(retryConfig) .afterFailedTryListener(s -> - { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); - }) + { + LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + }) .build(); Status<Trace> status = callExecutor.execute(callable); return status.getResult(); @@ -126,43 +126,43 @@ private Trace getTrace(List<String> traceIds) { } Trace traceRes = new Trace(); tracesResponse.traces().forEach(trace -> - { - if (trace.hasSegments()) { - 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()); - } - } - } catch (JsonProcessingException e) { - LOG.error("Failed to parse segment document: " + e.getMessage()); - throw new RuntimeException(e); + { + if (trace.hasSegments()) { + 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()); } - }); - } - }); + } + } catch (JsonProcessingException e) { + LOG.error("Failed to parse segment document: " + e.getMessage()); + throw new RuntimeException(e); + } + }); + } + }); return traceRes; } private void getNestedSubSegments(List<SubSegment> subsegments, Trace traceRes, List<String> idsToIgnore) { subsegments.forEach(subsegment -> - { - List<String> subSegmentIdsToIgnore = Collections.emptyList(); - if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { - traceRes.addSubSegment(subsegment); - if (subsegment.hasSubsegments()) { - subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId) - .collect(Collectors.toList()); - } - } + { + List<String> subSegmentIdsToIgnore = Collections.emptyList(); + if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { + traceRes.addSubSegment(subsegment); if (subsegment.hasSubsegments()) { - getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); + subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId) + .collect(Collectors.toList()); } - }); + } + if (subsegment.hasSubsegments()) { + getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); + } + }); } /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index d7301b149..82e7b9ead 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -14,18 +14,6 @@ package software.amazon.lambda.powertools.idempotency.persistence; -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.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.utils.StringUtils; -import software.amazon.lambda.powertools.core.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 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; @@ -37,7 +25,13 @@ import java.util.OptionalLong; import java.util.stream.Collectors; import java.util.stream.Stream; - +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.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.ConditionalCheckFailedException; import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; @@ -45,6 +39,11 @@ 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.idempotency.Constants; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; /** * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.<br> @@ -52,9 +51,8 @@ */ public class DynamoDBPersistenceStore extends BasePersistenceStore implements PersistenceStore { - private static final Logger LOG = LoggerFactory.getLogger(DynamoDBPersistenceStore.class); public static final String IDEMPOTENCY = "idempotency"; - + private static final Logger LOG = LoggerFactory.getLogger(DynamoDBPersistenceStore.class); private final String tableName; private final String keyAttr; private final String staticPkValue; @@ -97,7 +95,9 @@ private DynamoDBPersistenceStore(String tableName, if (idempotencyDisabledEnv == null || "false".equalsIgnoreCase(idempotencyDisabledEnv)) { this.dynamoDbClient = DynamoDbClient.builder() .httpClient(UrlConnectionHttpClient.builder().build()) - .overrideConfiguration(ClientOverrideConfiguration.builder().putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurator.getUserAgent(IDEMPOTENCY)).build()) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(IDEMPOTENCY)).build()) .region(Region.of(System.getenv(AWS_REGION_ENV))) .build(); } else { 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 758d7eb45..4e556966c 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 @@ -64,7 +64,7 @@ /** * Specify if S3 objects must be deleted after being processed (default = true) - Alternatively you might consider using S3 lifecycle policies to remove the payloads automatically after a period of time. + * Alternatively you might consider using S3 lifecycle policies to remove the payloads automatically after a period of time. */ boolean deleteS3Object() default true; } 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 861193203..2aa81691f 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 @@ -51,7 +51,8 @@ public Object around(ProceedingJoinPoint pjp, Optional<LargeMessageProcessor<?>> largeMessageProcessor = LargeMessageProcessorFactory.get(message); if (!largeMessageProcessor.isPresent()) { - LOG.warn("@LargeMessage annotation is placed on a method with unsupported message type [{}], proceeding", message.getClass()); + LOG.warn("@LargeMessage annotation is placed on a method with unsupported message type [{}], proceeding", + message.getClass()); return pjp.proceed(proceedArgs); } 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 f0e89e631..5478931f1 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 @@ -56,7 +56,8 @@ public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Th return pjp.proceed(proceedArgs); } // legacy attribute (sqs only) - payloadPointer = payloadPointer.replace("com.amazon.sqs.javamessaging.MessageS3Pointer", "software.amazon.payloadoffloading.PayloadS3Pointer"); + payloadPointer = payloadPointer.replace("com.amazon.sqs.javamessaging.MessageS3Pointer", + "software.amazon.payloadoffloading.PayloadS3Pointer"); if (LOG.isInfoEnabled()) { LOG.info("Large message [{}]: retrieving content from S3", getMessageId(message)); diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java index 18c99e300..7fd79b7c9 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeSQSMessageProcessor.java @@ -40,40 +40,6 @@ class LargeSQSMessageProcessor extends LargeMessageProcessor<SQSMessage> { private static final byte STRING_LIST_TYPE_FIELD_INDEX = 3; private static final byte BINARY_LIST_TYPE_FIELD_INDEX = 4; - @Override - protected String getMessageId(SQSMessage message) { - return message.getMessageId(); - } - - @Override - protected String getMessageContent(SQSMessage message) { - return message.getBody(); - } - - @Override - protected void updateMessageContent(SQSMessage message, String messageContent) { - message.setBody(messageContent); - // we update the MD5 digest so it doesn't look tempered - message.setMd5OfBody(calculateMessageBodyMd5(messageContent).orElse(message.getMd5OfBody())); - } - - @Override - protected boolean isLargeMessage(SQSMessage message) { - Map<String, MessageAttribute> msgAttributes = message.getMessageAttributes(); - return msgAttributes != null && (msgAttributes.containsKey(RESERVED_ATTRIBUTE_NAME) || msgAttributes.containsKey(LEGACY_RESERVED_ATTRIBUTE_NAME)); - } - - @Override - protected void removeLargeMessageAttributes(SQSMessage message) { - // message.getMessageAttributes() does not support remove operation, copy to new map - Map<String, MessageAttribute> newAttributes = new HashMap<>(message.getMessageAttributes()); - newAttributes.remove(RESERVED_ATTRIBUTE_NAME); - newAttributes.remove(LEGACY_RESERVED_ATTRIBUTE_NAME); - message.setMessageAttributes(newAttributes); - // we update the MD5 digest so it doesn't look tempered - message.setMd5OfMessageAttributes(calculateMessageAttributesMd5(newAttributes).orElse(message.getMd5OfMessageAttributes())); - } - /** * Compute the MD5 of the message body.<br/> * Inspired from {@code software.amazon.awssdk.services.sqs.internal.MessageMD5ChecksumInterceptor}.<br/> @@ -170,4 +136,40 @@ private static void updateLengthAndBytes(MessageDigest digest, ByteBuffer binary digest.update(lengthBytes.array()); digest.update(readOnlyBuffer); } + + @Override + protected String getMessageId(SQSMessage message) { + return message.getMessageId(); + } + + @Override + protected String getMessageContent(SQSMessage message) { + return message.getBody(); + } + + @Override + protected void updateMessageContent(SQSMessage message, String messageContent) { + message.setBody(messageContent); + // we update the MD5 digest so it doesn't look tempered + message.setMd5OfBody(calculateMessageBodyMd5(messageContent).orElse(message.getMd5OfBody())); + } + + @Override + protected boolean isLargeMessage(SQSMessage message) { + Map<String, MessageAttribute> msgAttributes = message.getMessageAttributes(); + return msgAttributes != null && (msgAttributes.containsKey(RESERVED_ATTRIBUTE_NAME) || + msgAttributes.containsKey(LEGACY_RESERVED_ATTRIBUTE_NAME)); + } + + @Override + protected void removeLargeMessageAttributes(SQSMessage message) { + // message.getMessageAttributes() does not support remove operation, copy to new map + Map<String, MessageAttribute> newAttributes = new HashMap<>(message.getMessageAttributes()); + newAttributes.remove(RESERVED_ATTRIBUTE_NAME); + newAttributes.remove(LEGACY_RESERVED_ATTRIBUTE_NAME); + message.setMessageAttributes(newAttributes); + // we update the MD5 digest so it doesn't look tempered + message.setMd5OfMessageAttributes( + calculateMessageAttributesMd5(newAttributes).orElse(message.getMd5OfMessageAttributes())); + } } 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 95dfd445a..c364a89d9 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 @@ -65,7 +65,9 @@ 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; @@ -89,7 +91,8 @@ private String processSQSMessage(SQSMessage sqsMessage, Context context) { } @LargeMessage - private String processSQSMessageWithMd5Checks(SQSMessage transformedMessage, String initialBodyMD5, String initialAttributesMD5) { + private String processSQSMessageWithMd5Checks(SQSMessage transformedMessage, String initialBodyMD5, + String initialAttributesMD5) { assertThat(transformedMessage.getMd5OfBody()).isNotEqualTo(initialBodyMD5); assertThat(transformedMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); @@ -133,7 +136,8 @@ 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); @@ -156,7 +160,8 @@ public void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { binAttribute.setDataType("Binary"); MessageAttribute listBinAttribute = new MessageAttribute(); - listBinAttribute.setBinaryListValues(Collections.singletonList(ByteBuffer.wrap("customAttributeValue".getBytes(StandardCharsets.UTF_8)))); + listBinAttribute.setBinaryListValues( + Collections.singletonList(ByteBuffer.wrap("customAttributeValue".getBytes(StandardCharsets.UTF_8)))); listBinAttribute.setDataType("BinaryList"); Map<String, MessageAttribute> attrs = new HashMap<>(); @@ -166,7 +171,8 @@ public void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true, attrs); // when - String message = processSQSMessageWithMd5Checks(sqsMessage, sqsMessage.getMd5OfBody(), sqsMessage.getMd5OfMessageAttributes()); + String message = processSQSMessageWithMd5Checks(sqsMessage, sqsMessage.getMd5OfBody(), + sqsMessage.getMd5OfMessageAttributes()); // then assertThat(message).isEqualTo(BIG_MSG); @@ -186,7 +192,8 @@ 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); @@ -263,7 +270,8 @@ public void testNullMessage_shouldProceedWithoutS3() { @Test public 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))); + when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", + new Exception("User is not allowed to access bucket " + BUCKET_NAME))); SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); // when / then @@ -276,7 +284,8 @@ public void testGetS3ObjectException_shouldThrowLargeMessageProcessingException( public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingException() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - when(s3Client.deleteObject(any(DeleteObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", new Exception("User is not allowed to access bucket " + BUCKET_NAME))); + when(s3Client.deleteObject(any(DeleteObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", + new Exception("User is not allowed to access bucket " + BUCKET_NAME))); SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); // when / then @@ -286,18 +295,21 @@ public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingExcepti } private ResponseInputStream<GetObjectResponse> s3ObjectWithLargeMessage() { - return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); + return new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); } private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage) { return sqsMessageWithBody(messageBody, largeMessage, null); } - private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, Map<String, MessageAttribute> optionalAttributes) { + private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, + Map<String, MessageAttribute> optionalAttributes) { SQSMessage sqsMessage = new SQSMessage(); sqsMessage.setBody(messageBody); if (messageBody != null) { - sqsMessage.setMd5OfBody(calculateMessageBodyMd5(messageBody).orElseThrow(() -> new RuntimeException("Unable to md5 body " + messageBody))); + sqsMessage.setMd5OfBody(calculateMessageBodyMd5(messageBody).orElseThrow( + () -> new RuntimeException("Unable to md5 body " + messageBody))); } if (largeMessage) { @@ -311,7 +323,8 @@ private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, attributeMap.put(LargeMessageProcessor.RESERVED_ATTRIBUTE_NAME, payloadAttribute); sqsMessage.setMessageAttributes(attributeMap); - sqsMessage.setMd5OfMessageAttributes(calculateMessageAttributesMd5(attributeMap).orElseThrow(() -> new RuntimeException("Unable to md5 attributes " + attributeMap))); + sqsMessage.setMd5OfMessageAttributes(calculateMessageAttributesMd5(attributeMap).orElseThrow( + () -> new RuntimeException("Unable to md5 attributes " + attributeMap))); } return sqsMessage; } @@ -319,7 +332,8 @@ private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, private SNSRecord snsRecordWithMessage(String messageBody, boolean largeMessage) { SNS sns = new SNS().withMessage(messageBody); if (largeMessage) { - sns.setMessageAttributes(Collections.singletonMap(LargeMessageProcessor.RESERVED_ATTRIBUTE_NAME, new SNSEvent.MessageAttribute())); + sns.setMessageAttributes(Collections.singletonMap(LargeMessageProcessor.RESERVED_ATTRIBUTE_NAME, + new SNSEvent.MessageAttribute())); } return new SNSRecord().withSns(sns); } 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 index c2c13c86f..fd646ab50 100644 --- 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 @@ -163,11 +163,11 @@ private Map<String, Object> resolveAdditionalFields(LogEvent logEvent) { // Go over MDC logEvent.getContextData().forEach((key, value) -> - { - if (Strings.isNotBlank(key) && value != null) { - additionalFieldsMap.put(key, value); - } - }); + { + if (Strings.isNotBlank(key) && value != null) { + additionalFieldsMap.put(key, value); + } + }); return additionalFieldsMap; } 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 index c7b7c5d53..dc9816932 100644 --- 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 @@ -40,12 +40,12 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { // Inject all the context information. ReadOnlyStringMap contextData = logEvent.getContextData(); contextData.forEach((key, value) -> - { - jsonWriter.writeSeparator(); - jsonWriter.writeString(key); - stringBuilder.append(':'); - jsonWriter.writeValue(value); - }); + { + jsonWriter.writeSeparator(); + jsonWriter.writeString(key); + stringBuilder.append(':'); + jsonWriter.writeValue(value); + }); } }; } 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 index 47b495da3..9b0c6165a 100644 --- 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 @@ -83,15 +83,15 @@ void shouldModifyLogLevelBasedOnEnvVariable() 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"); - }); + { + 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 @@ -103,19 +103,19 @@ void shouldModifyLogLevelBasedOnSamplingRule() throws IOException { 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"); - }); + { + 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) 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 index 1da100f26..09517d46e 100644 --- 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 @@ -96,10 +96,10 @@ public static void withSingleMetric(final String name, final Unit unit, final Consumer<MetricsLogger> logger) { withMetricsLogger(metricsLogger -> - { - metricsLogger.putMetric(name, value, unit); - logger.accept(metricsLogger); - }); + { + metricsLogger.putMetric(name, value, unit); + logger.accept(metricsLogger); + }); } /** @@ -119,11 +119,11 @@ public static void withSingleMetric(final String name, final String namespace, final Consumer<MetricsLogger> logger) { withMetricsLogger(metricsLogger -> - { - metricsLogger.setNamespace(namespace); - metricsLogger.putMetric(name, value, unit); - logger.accept(metricsLogger); - }); + { + metricsLogger.setNamespace(namespace); + metricsLogger.putMetric(name, value, unit); + logger.accept(metricsLogger); + }); } /** 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 index da4162ea0..7f234a4d6 100644 --- 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 @@ -72,20 +72,20 @@ void singleMetricsCaptureUtilityWithDefaultDimension() { 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"); - }); + { + Map<String, Object> logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "Booking") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + }); } } @@ -103,15 +103,15 @@ void singleMetricsCaptureUtility() { 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> logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + }); } } @@ -130,21 +130,21 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { assertThat(out.toString()) .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(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"); + 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"); + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=GlobalName"); - }); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); } } @@ -175,28 +175,28 @@ private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); methodToTest.accept(metricsLogger -> - { - metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); - metricsLogger.putMetric("Metric1", 1, Unit.COUNT); - }); + { + metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); + metricsLogger.putMetric("Metric1", 1, Unit.COUNT); + }); assertThat(out.toString()) .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(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"); + 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"); + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=GlobalName"); - }); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); } } 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 index 5d7fb7120..761c20caa 100644 --- 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 @@ -38,8 +38,8 @@ public Object handleRequest(Object input, Context context) { 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 index 0a0079b80..d968f94f5 100644 --- 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 @@ -37,8 +37,8 @@ public Object handleRequest(Object input, Context context) { 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/internal/LambdaMetricsAspectTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java index 44202b8b8..eaddfa75d 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 @@ -100,30 +100,30 @@ public void metricsWithoutColdStart() { 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"); - }); + { + 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"); + }); } } @@ -144,30 +144,30 @@ public void metricsWithDefaultDimensionSpecified() { 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"); - }); + { + 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"); + }); } } @@ -188,28 +188,28 @@ public void metricsWithDefaultNoDimensionSpecified() { assertThat(out.toString().split("\n")) .hasSize(2) .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); + { + Map<String, Object> logAsJson = readAsJson(s[0]); - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); + assertThat(logAsJson) + .containsEntry("Metric2", 1.0) + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") + .containsEntry("function_request_id", "123ABC"); - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); - logAsJson = readAsJson(s[1]); + logAsJson = readAsJson(s[1]); - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -228,25 +228,25 @@ public void metricsWithColdStart() { assertThat(out.toString().split("\n")) .hasSize(2) .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"); - }); + { + 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"); + }); } } @@ -265,34 +265,34 @@ public void noColdStartMetricsWhenColdStartDone() { 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"); - }); + { + 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"); + }); } } @@ -309,15 +309,15 @@ public void metricsWithStreamHandler() throws IOException { assertThat(out.toString()) .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + { + Map<String, Object> logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -348,13 +348,13 @@ public void noExceptionWhenNoMetricsEmitted() { assertThat(out.toString()) .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); + { + Map<String, Object> logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("Service", "booking") - .doesNotContainKey("_aws"); - }); + assertThat(logAsJson) + .containsEntry("Service", "booking") + .doesNotContainKey("_aws"); + }); } } @@ -369,13 +369,13 @@ public void allowWhenNoDimensionsSet() { assertThat(out.toString()) .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("CoolMetric", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + { + Map<String, Object> logAsJson = readAsJson(s); + assertThat(logAsJson) + .containsEntry("CoolMetric", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -409,14 +409,14 @@ public void metricsPublishedEvenHandlerThrowsException() { 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"); - }); + { + Map<String, Object> logAsJson = readAsJson(s); + assertThat(logAsJson) + .containsEntry("CoolMetric", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } 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 index 0df05f875..f2e4faebb 100644 --- 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 @@ -157,7 +157,8 @@ public AppConfigProvider build() { .httpClientBuilder(UrlConnectionHttpClient.builder()) .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) .build(); } 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 e6481c5da..edb82f5ec 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 @@ -129,15 +129,15 @@ public Map<String, String> getMultiple(String path) { String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { return (Map<String, String>) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> - { - Map<String, String> params = getMultipleValues(pathWithoutTrailingSlash); + { + Map<String, String> params = getMultipleValues(pathWithoutTrailingSlash); - cacheManager.putInCache(pathWithoutTrailingSlash, params); + cacheManager.putInCache(pathWithoutTrailingSlash, params); - params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); + params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); - return params; - }); + return params; + }); } finally { resetToDefaults(); } @@ -158,18 +158,18 @@ public Map<String, String> getMultiple(String path) { public String get(final String key) { try { return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { - String value = getValue(key); + { + String value = getValue(key); - String transformedValue = value; - if (transformationManager != null && transformationManager.shouldTransform()) { - transformedValue = transformationManager.performBasicTransformation(value); - } + String transformedValue = value; + if (transformationManager != null && transformationManager.shouldTransform()) { + transformedValue = transformationManager.performBasicTransformation(value); + } - cacheManager.putInCache(key, transformedValue); + cacheManager.putInCache(key, transformedValue); - return transformedValue; - }); + return transformedValue; + }); } finally { // in all case, we reset options to default, for next call resetToDefaults(); @@ -192,19 +192,19 @@ public String get(final String key) { public <T> T get(final String key, final Class<T> targetClass) { try { return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { - String value = getValue(key); + { + String value = getValue(key); - if (transformationManager == null) { - throw new IllegalStateException( - "Trying to transform value while no TransformationManager has been provided."); - } - T transformedValue = transformationManager.performComplexTransformation(value, targetClass); + if (transformationManager == null) { + throw new IllegalStateException( + "Trying to transform value while no TransformationManager has been provided."); + } + T transformedValue = transformationManager.performComplexTransformation(value, targetClass); - cacheManager.putInCache(key, transformedValue); + cacheManager.putInCache(key, transformedValue); - return transformedValue; - }); + return transformedValue; + }); } finally { // in all case, we reset options to default, for next call resetToDefaults(); 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 index 499241927..3a8732e18 100644 --- 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 @@ -109,14 +109,14 @@ protected Map<String, String> getMultipleValues(String path) { .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()); - } - }) + { + 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(), @@ -135,7 +135,9 @@ 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()) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) .build(); } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index 4cfd8f899..549cdfbab 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -208,19 +208,19 @@ private Map<String, String> getMultipleBis(String path, String nextToken) { 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. */ - String name = parameter.name(); - if (name.startsWith(path)) { - name = name.replaceFirst(path, ""); - } - name = name.replaceFirst("/", ""); - params.put(name, parameter.value()); - }); + String name = parameter.name(); + if (name.startsWith(path)) { + name = name.replaceFirst(path, ""); + } + name = name.replaceFirst("/", ""); + params.put(name, parameter.value()); + }); } if (!StringUtils.isEmpty(res.nextToken())) { @@ -251,7 +251,9 @@ 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()) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) .build(); } 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 index 788367ea8..2612f6c7f 100644 --- 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 @@ -161,7 +161,9 @@ 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()) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) .build(); } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java index abfc9ab8a..2cf84dad4 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java @@ -123,9 +123,9 @@ public void getValueWithMalformedRowThrows() { .build()); // Act Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { - provider.getValue(key); - }); + { + provider.getValue(key); + }); } @@ -190,10 +190,10 @@ public void getMultipleValuesMissingSortKey_throwsException() { // Assert Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { - // Act - provider.getMultipleValues(key); - }); + { + // Act + provider.getMultipleValues(key); + }); } @Test @@ -211,10 +211,10 @@ public void getValuesWithMalformedRowThrows() { // Assert Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { - // Act - provider.getMultipleValues(key); - }); + { + // Act + provider.getMultipleValues(key); + }); } @Test diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java index 6a5aa3e68..2a4f8f927 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java @@ -186,11 +186,11 @@ public void getMultipleWithNextToken() { 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(req.path()).isEqualTo("/prod/app1"); + assertThat(req.withDecryption()).isFalse(); + assertThat(req.recursive()).isFalse(); + }); assertThat(request1.nextToken()).isNull(); assertThat(request2.nextToken()).isEqualTo("123abc"); 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 index d346a1aa4..2c246336b 100644 --- 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 @@ -97,9 +97,9 @@ public void testWithComplexTransform() { public void testWithComplexTransformWrongTargetClass_ShouldThrowException() { assertThatExceptionOfType(TransformationException.class) .isThrownBy(() -> - { - AnotherObject obj = wrongTransform; - }); + { + AnotherObject obj = wrongTransform; + }); } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java index 13ad4d28f..1b3f158be 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java @@ -258,14 +258,14 @@ public <T> List<T> asListOf(Class<T> clazz) { } } else { return contentList.stream().map(s -> - { - try { - return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); - } catch (IOException e) { - throw new EventDeserializationException( - "Cannot load the event as a list of " + clazz.getSimpleName(), e); - } - }).collect(Collectors.toList()); + { + try { + return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); + } catch (IOException e) { + throw new EventDeserializationException( + "Cannot load the event as a list of " + clazz.getSimpleName(), e); + } + }).collect(Collectors.toList()); } } } 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 index 1f00edf17..d89642780 100644 --- 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 @@ -595,11 +595,11 @@ public static <R> List<R> batchProcessor(final SQSEvent event, 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()); - }); + { + 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, 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 index 57ddeb22f..70cf04fb8 100644 --- 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 @@ -84,16 +84,16 @@ public final <T> void processSuccessAndHandleFailed(final List<T> successReturns 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); - } - }); + { + boolean nonRetryableException = isNonRetryableException(exception, nonRetryableExceptions); + + if (nonRetryableException) { + nonRetryableMessageToException.put(sqsMessage, exception); + } else { + exceptions.add(exception); + failedMessages.add(sqsMessage); + } + }); } List<SQSMessage> messagesToBeDeleted = new ArrayList<>(success); @@ -151,46 +151,46 @@ private boolean moveNonRetryableMessagesToDlqIfConfigured( List<SendMessageBatchRequestEntry> dlqMessages = nonRetryableMessageToException.keySet().stream() .map(sqsMessage -> - { - Map<String, MessageAttributeValue> messageAttributesMap = new HashMap<>(); + { + Map<String, MessageAttributeValue> messageAttributesMap = new HashMap<>(); - sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> - { - MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); + sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> + { + MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); - builder - .dataType(messageAttribute.getDataType()) - .stringValue(messageAttribute.getStringValue()); + builder + .dataType(messageAttribute.getDataType()) + .stringValue(messageAttribute.getStringValue()); - if (null != messageAttribute.getBinaryValue()) { - builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); - } + if (null != messageAttribute.getBinaryValue()) { + builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); + } - messageAttributesMap.put(s, builder.build()); - }); + messageAttributesMap.put(s, builder.build()); + }); - return SendMessageBatchRequestEntry.builder() - .messageBody(sqsMessage.getBody()) - .id(sqsMessage.getMessageId()) - .messageAttributes(messageAttributesMap) - .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()); + SendMessageBatchResponse sendMessageBatchResponse = + client.sendMessageBatch(SendMessageBatchRequest.builder() + .entries(entriesToSend) + .queueUrl(dlqUrl.get()) + .build()); - LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); + LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); - return sendMessageBatchResponse; - }); + return sendMessageBatchResponse; + }); return sendMessageBatchResponses.stream() .filter(response -> null != response && response.hasFailed()) @@ -206,32 +206,32 @@ private Optional<String> fetchDlqUrl(Map<SQSMessage, Exception> nonRetryableMess .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); - })); + { + 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() { @@ -248,18 +248,18 @@ private void deleteMessagesFromQueue(final List<SQSMessage> messages) { .build()).collect(toList()); batchRequest(entries, 10, entriesToDelete -> - { - DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder() - .queueUrl(url(messages.get(0).getEventSourceArn())) - .entries(entriesToDelete) - .build(); + { + DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder() + .queueUrl(url(messages.get(0).getEventSourceArn())) + .entries(entriesToDelete) + .build(); - DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); + DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); - LOG.debug("Response from delete request {}", deleteMessageBatchResponse); + LOG.debug("Response from delete request {}", deleteMessageBatchResponse); - return deleteMessageBatchResponse; - }); + return deleteMessageBatchResponse; + }); } } 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 index 7022e399a..e5176e13a 100644 --- 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 @@ -58,16 +58,16 @@ public static List<PayloadS3Pointer> processMessages(final List<SQSMessage> reco sqsMessage.getBody()))); ResponseInputStream<GetObjectResponse> s3Object = callS3Gracefully(s3Pointer, pointer -> - { - ResponseInputStream<GetObjectResponse> response = - s3Client().getObject(GetObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); + { + ResponseInputStream<GetObjectResponse> response = + s3Client().getObject(GetObjectRequest.builder() + .bucket(pointer.getS3BucketName()) + .key(pointer.getS3Key()) + .build()); - LOG.debug("Object downloaded with key: " + s3Pointer.getS3Key()); - return response; - }); + LOG.debug("Object downloaded with key: " + s3Pointer.getS3Key()); + return response; + }); sqsMessage.setBody(readStringFromS3Object(s3Object, s3Pointer)); s3Pointers.add(s3Pointer); @@ -95,14 +95,14 @@ private static String readStringFromS3Object(ResponseInputStream<GetObjectRespon 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; - }); + { + 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, 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 index 42e4b9d8f..c0c334e78 100644 --- 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 @@ -61,10 +61,10 @@ void setUp() throws IOException { @Test void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { List<String> returnValues = batchProcessor(event, false, (message) -> - { - interactionClient.listQueues(); - return "Success"; - }); + { + interactionClient.listQueues(); + return "Success"; + }); assertThat(returnValues) .hasSize(2) @@ -92,34 +92,34 @@ void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; SqsMessageHandler<String> failedHandler = (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } + { + if (failedId.equals(message.getMessageId())) { + throw new RuntimeException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }; + interactionClient.listQueues(); + return "Success"; + }; assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, failedHandler)) .satisfies(e -> - { + { - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains(failedId); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains(failedId); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); verify(interactionClient).listQueues(); @@ -133,30 +133,30 @@ void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { @Test void shouldBatchProcessAndFullFailuresInBatch() { SqsMessageHandler<String> failedHandler = (message) -> - { - throw new RuntimeException(message.getMessageId()); - }; + { + throw new RuntimeException(message.getMessageId()); + }; assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, failedHandler)) .satisfies(e -> - { + { - assertThat(e.successMessageReturnValues()) - .isEmpty(); + assertThat(e.successMessageReturnValues()) + .isEmpty(); - assertThat(e.getFailures()) - .hasSize(2) - .extracting("messageId") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); + 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"); - }); + assertThat(e.getExceptions()) + .hasSize(2) + .extracting("detailMessage") + .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", + "2e1424d4-f796-459a-8184-9c92662be6da"); + }); verifyNoInteractions(sqsClient); } @@ -166,22 +166,22 @@ void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, FailureSampleInnerSqsHandler.class)) .satisfies(e -> - { + { - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); } @@ -192,14 +192,14 @@ void shouldBatchProcessAndSuppressExceptions() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; SqsMessageHandler<String> failedHandler = (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } + { + if (failedId.equals(message.getMessageId())) { + throw new RuntimeException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }; + interactionClient.listQueues(); + return "Success"; + }; List<String> returnValues = batchProcessor(event, true, failedHandler); @@ -239,14 +239,14 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { .build()); List<String> batchProcessor = batchProcessor(event, (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } + { + if (failedId.equals(message.getMessageId())) { + throw new IllegalStateException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }, IllegalStateException.class, IllegalArgumentException.class); + interactionClient.listQueues(); + return "Success"; + }, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -270,14 +270,14 @@ void shouldBatchProcessAndDeleteNonRetryableException() { .build()); List<String> batchProcessor = batchProcessor(event, false, (message) -> - { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } + { + if (failedId.equals(message.getMessageId())) { + throw new IllegalStateException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }, true, IllegalStateException.class, IllegalArgumentException.class); + interactionClient.listQueues(); + return "Success"; + }, true, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -294,22 +294,22 @@ void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(batch25Message, FailureSampleInnerSqsHandler.class)) .satisfies(e -> - { + { - assertThat(e.successMessageReturnValues()) - .hasSize(24) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(24) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); ArgumentCaptor<DeleteMessageBatchRequest> captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); @@ -339,14 +339,14 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() t .build()); List<String> batchProcessor = batchProcessor(batch25Message, (message) -> - { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - interactionClient.listQueues(); - return "Success"; - } + { + if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { + interactionClient.listQueues(); + return "Success"; + } - throw new IllegalStateException("Failed processing"); - }, IllegalStateException.class, IllegalArgumentException.class); + throw new IllegalStateException("Failed processing"); + }, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); 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 index 53beeefcb..bfc555405 100644 --- 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 @@ -76,10 +76,10 @@ public void processWholeBatch() { // Act AtomicInteger processedCount = new AtomicInteger(); List<Object> results = batchProcessor(sqsBatchEvent, false, (message) -> - { - processedCount.getAndIncrement(); - return true; - }); + { + processedCount.getAndIncrement(); + return true; + }); // Assert assertThat(processedCount.get()).isEqualTo(3); @@ -103,26 +103,26 @@ public void singleFailureInMiddleOfBatch() { 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; - })) + { + 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()); - }); + { + 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() @@ -146,13 +146,13 @@ public void singleFailureAtEndOfBatch() { 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; - })); + { + int value = processedCount.getAndIncrement(); + if (value == 2) { + throw new RuntimeException("Whoops"); + } + return true; + })); // Assert DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); @@ -171,18 +171,18 @@ public void messageFailureStopsGroupProcessing() { assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(sqsBatchEvent, (message) -> - { - String groupId = message.getAttributes().get("MessageGroupId"); - if (groupId.equals(groupToFail)) { - throw new RuntimeException("Failed processing"); - } - return groupId; - })) + { + 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(); - }); + { + 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 index d3b675371..afc426976 100644 --- 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 @@ -86,11 +86,11 @@ public void testLargeMessage() { "\",\"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; - }); + { + Map<String, String> someBusinessLogic = new HashMap<>(); + someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); + return someBusinessLogic; + }); assertThat(sqsMessage) .hasSize(1) @@ -102,13 +102,13 @@ public void testLargeMessage() { Assertions.assertThat(delete.getValue()) .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } @ParameterizedTest @@ -125,11 +125,11 @@ public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { "\",\"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; - }); + { + Map<String, String> someBusinessLogic = new HashMap<>(); + someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); + return someBusinessLogic; + }); assertThat(sqsMessage) .hasSize(1) @@ -141,13 +141,13 @@ public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { Assertions.assertThat(delete.getValue()) .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } else { verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); } @@ -164,11 +164,11 @@ public void shouldNotProcessSmallMessageBody() { 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; - }); + { + Map<String, String> someBusinessLogic = new HashMap<>(); + someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); + return someBusinessLogic; + }); assertThat(sqsMessage) .containsEntry("Message", "This is small message"); 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 index ff04aba25..535da11c7 100644 --- 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 @@ -101,13 +101,13 @@ public void testLargeMessage() { Assertions.assertThat(delete.getValue()) .satisfies((Consumer<DeleteObjectRequest>) deleteObjectRequest -> - { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } @Test 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 index b257c1962..c0211cb83 100644 --- 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 @@ -86,21 +86,21 @@ void shouldBatchProcessMessageWithSuccessDeletedOnFailureInBatchFromSQS() { 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"); - }); + { + 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)); @@ -218,21 +218,21 @@ void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { 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"); - }); + { + 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)); @@ -258,21 +258,21 @@ void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionWhenFailedPar 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"); - }); + { + 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)); @@ -301,21 +301,21 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqAndThrowException() thro 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"); - }); + { + 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)); 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 index 55349b267..9e9c464a6 100644 --- 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 @@ -108,14 +108,14 @@ public void testThatLoggingAnnotationActsLast() throws IOException { assertThat(Files.lines(Paths.get("target/logfile.json"))) .hasSize(2) .satisfies(line -> - { - Map<String, Object> actual = parseToMap(line.get(0)); + { + Map<String, Object> actual = parseToMap(line.get(0)); - String message = actual.get("message").toString(); + String message = actual.get("message").toString(); - assertThat(message) - .contains("A big message"); - }); + assertThat(message) + .contains("A big message"); + }); } @Test 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 69054d0c6..78283fbc2 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 @@ -96,31 +96,31 @@ void shouldInvokeCodeBlockWrappedWithinSubsegment() { Context test = mock(Context.class); TracingUtils.withSubsegment("testSubSegment", subsegment -> - { - subsegment.putAnnotation("key", "val"); - subsegment.putMetadata("key", "val"); - test.getFunctionName(); - }); + { + subsegment.putAnnotation("key", "val"); + subsegment.putMetadata("key", "val"); + test.getFunctionName(); + }); verify(test).getFunctionName(); assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("service_undefined"); + assertThat(subsegment.getNamespace()) + .isEqualTo("service_undefined"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); - assertThat(subsegment.getMetadata()) - .hasSize(1); - }); + assertThat(subsegment.getMetadata()) + .hasSize(1); + }); } @Test @@ -128,31 +128,31 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { Context test = mock(Context.class); TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> - { - subsegment.putAnnotation("key", "val"); - subsegment.putMetadata("key", "val"); - test.getFunctionName(); - }); + { + subsegment.putAnnotation("key", "val"); + subsegment.putMetadata("key", "val"); + test.getFunctionName(); + }); verify(test).getFunctionName(); assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("testNamespace"); + assertThat(subsegment.getNamespace()) + .isEqualTo("testNamespace"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); - assertThat(subsegment.getMetadata()) - .hasSize(1); - }); + assertThat(subsegment.getMetadata()) + .hasSize(1); + }); } @Test @@ -162,10 +162,10 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce Entity traceEntity = AWSXRay.getTraceEntity(); Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> - { - subsegment.putAnnotation("key", "val"); - test.getFunctionName(); - })); + { + subsegment.putAnnotation("key", "val"); + test.getFunctionName(); + })); thread.start(); thread.join(); @@ -175,17 +175,17 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("service_undefined"); + assertThat(subsegment.getNamespace()) + .isEqualTo("service_undefined"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); - }); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); + }); } @Test @@ -196,10 +196,10 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter Thread thread = new Thread(() -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> - { - subsegment.putAnnotation("key", "val"); - test.getFunctionName(); - })); + { + subsegment.putAnnotation("key", "val"); + test.getFunctionName(); + })); thread.start(); thread.join(); @@ -209,16 +209,16 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("testNamespace"); + assertThat(subsegment.getNamespace()) + .isEqualTo("testNamespace"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); - }); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .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 5e3ec6545..d61206886 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 @@ -120,16 +120,16 @@ void shouldCaptureTraces() { 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.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } @Test @@ -144,20 +144,20 @@ void shouldCaptureTracesWithExceptionMetaData() { 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)); - }); + { + 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)); + }); } @Test @@ -170,16 +170,16 @@ void shouldCaptureTracesForStream() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "streamHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("streamHandler"); - }); + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "streamHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("streamHandler"); + }); } @Test @@ -212,15 +212,15 @@ void shouldCaptureTracesWithNoMetadata() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -235,15 +235,15 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -258,15 +258,15 @@ void shouldCaptureTracesWithNoMetadataDeprecated() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -283,15 +283,15 @@ void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } } @@ -309,16 +309,16 @@ void shouldCaptureTracesIfExplicitlyEnabledAndEnvironmentVariableIsDisabled() { 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.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } } @@ -334,15 +334,15 @@ void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); + { + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); - assertThat(subsegment.getMetadata().get("lambdaHandler")) - .hasFieldOrPropertyWithValue("handleRequest response", - "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}"); - }); + assertThat(subsegment.getMetadata().get("lambdaHandler")) + .hasFieldOrPropertyWithValue("handleRequest response", + "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}"); + }); assertThatNoException().isThrownBy(AWSXRay::endSegment); @@ -366,16 +366,16 @@ void shouldCaptureTracesIfExplicitlyEnabledBothAndEnvironmentVariableIsDisabled( 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.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } } @@ -397,15 +397,15 @@ void shouldNotCaptureTracesWithExceptionMetaDataIfDisabledViaEnvironmentVariable assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) .allSatisfy(subsegment -> - { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } } @@ -424,20 +424,20 @@ void shouldCaptureTracesWithExceptionMetaDataEnabledExplicitlyAndEnvironmentVari 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)); - }); + { + 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-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 86dddd3e2..fa0d1394c 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 @@ -65,9 +65,9 @@ public void testLoadMetaSchema_NoValidation() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); assertThatNoException().isThrownBy(() -> - { - getJsonSchema("classpath:/schema_v7_ko.json", false); - }); + { + getJsonSchema("classpath:/schema_v7_ko.json", false); + }); } @Test @@ -118,9 +118,9 @@ public void testValidateJsonNodeOK() throws IOException { ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ok.json")); assertThatNoException().isThrownBy(() -> - { - validate(node, schemaString); - }); + { + validate(node, schemaString); + }); } @Test @@ -140,9 +140,9 @@ public void testValidateMapOK() { map.put("price", 258); assertThatNoException().isThrownBy(() -> - { - validate(map, schemaString); - }); + { + validate(map, schemaString); + }); } @Test @@ -167,9 +167,9 @@ public void testValidateStringOK() { String json = "{\n \"id\": 43242,\n \"name\": \"FooBar XY\",\n \"price\": 258\n}"; assertThatNoException().isThrownBy(() -> - { - validate(json, schemaString); - }); + { + validate(json, schemaString); + }); } @Test @@ -184,9 +184,9 @@ public void testValidateObjectOK() { Product product = new Product(42, "FooBar", 42); assertThatNoException().isThrownBy(() -> - { - validate(product, schemaString); - }); + { + validate(product, schemaString); + }); } @Test @@ -211,9 +211,9 @@ public void testValidateSubObjectOK() { MyCustomEvent event = new MyCustomEvent(basket); assertThatNoException().isThrownBy(() -> - { - validate(event, schemaString, "basket.products[0]"); - }); + { + validate(event, schemaString, "basket.products[0]"); + }); } @Test 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 9ea596ff3..c8d5e7ade 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 @@ -110,9 +110,9 @@ public void testValidateOutboundJsonSchema(Object object) throws Throwable { when(validation.outboundSchema()).thenReturn("classpath:/schema_v7.json"); assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> - { - validationAspect.around(pjp, validation); - }); + { + validationAspect.around(pjp, validation); + }); } @Test From 788d2ce649eb33efbad0c0e038415b80945be0a4 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 4 Aug 2023 14:34:58 +0100 Subject: [PATCH 0416/1008] Add v2 to workflow (#1341) --- .github/workflows/build-docs.yml | 1 + .github/workflows/build.yml | 1 + .github/workflows/run-e2e-tests.yml | 4 +++- .github/workflows/spotbugs.yml | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index cbcdcdd9e..f4a9c4d3f 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - v2 paths: - 'docs/**' - 'mkdocs.yml' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a4cc8daa..9ed871a6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - v2 paths: - 'powertools-cloudformation/**' - 'powertools-core/**' diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index a998e6b2d..36c9dd97a 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -4,7 +4,9 @@ on: workflow_dispatch: push: - branches: [main] + branches: + - main + - v2 paths: # add other modules when there are under e2e tests - 'powertools-e2e-tests/**' - 'powertools-core/**' diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 561300810..0b07bcd81 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - v2 paths: - 'powertools-cloudformation/**' - 'powertools-core/**' From be04ef844d441c00ff46b9e22ff5ab633ac0fb0b Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:23:13 +0100 Subject: [PATCH 0417/1008] chore: Start V2 branch (#1346) * Initial commit * Add warning --- README.md | 4 +- docs/utilities/large_messages.md | 6 - docs/utilities/sqs_batch.md | 489 ------------- docs/utilities/sqs_large_message_handling.md | 296 -------- examples/pom.xml | 1 - examples/powertools-examples-sqs/README.md | 56 -- .../powertools-examples-sqs/events/event.json | 63 -- examples/powertools-examples-sqs/pom.xml | 196 ------ .../java/org/demo/sqs/SqsMessageSender.java | 92 --- .../src/main/java/org/demo/sqs/SqsPoller.java | 76 -- .../src/main/resources/log4j2.xml | 16 - .../powertools-examples-sqs/template.yaml | 148 ---- mkdocs.yml | 3 - pom.xml | 2 - powertools-sqs/pom.xml | 144 ---- .../sqs/SQSBatchProcessingException.java | 93 --- .../lambda/powertools/sqs/SqsBatch.java | 104 --- .../powertools/sqs/SqsLargeMessage.java | 85 --- .../powertools/sqs/SqsMessageHandler.java | 44 -- .../lambda/powertools/sqs/SqsUtils.java | 653 ------------------ ...ippedMessageDueToFailedBatchException.java | 26 - .../powertools/sqs/internal/BatchContext.java | 281 -------- .../sqs/internal/SqsLargeMessageAspect.java | 170 ----- .../SqsMessageBatchProcessorAspect.java | 55 -- .../powertools/sqs/SampleSqsHandler.java | 26 - .../sqs/SqsUtilsBatchProcessorTest.java | 386 ----------- .../sqs/SqsUtilsFifoBatchProcessorTest.java | 188 ----- .../sqs/SqsUtilsLargeMessageTest.java | 228 ------ .../sqs/handlers/LambdaHandlerApiGateway.java | 32 - .../PartialBatchFailureSuppressedHandler.java | 46 -- .../PartialBatchPartialFailureHandler.java | 46 -- .../handlers/PartialBatchSuccessHandler.java | 42 -- .../sqs/handlers/SqsMessageHandler.java | 29 - ...MessageHandlerWithNonRetryableHandler.java | 52 -- ...dlerWithNonRetryableHandlerWithDelete.java | 53 -- .../handlers/SqsNoDeleteMessageHandler.java | 29 - .../internal/SqsLargeMessageAspectTest.java | 222 ------ .../SqsMessageBatchProcessorAspectTest.java | 331 --------- .../src/test/resources/SqsFifoBatchEvent.json | 73 -- .../test/resources/sampleSqsBatchEvent.json | 36 - .../sampleSqsBatchEventBatchSize25.json | 404 ----------- .../resources/threeMessageSqsBatchEvent.json | 70 -- powertools-test-suite/pom.xml | 164 ----- .../testsuite/LoggingOrderTest.java | 193 ------ .../handler/LoggingOrderMessageHandler.java | 31 - .../TracingLoggingStreamMessageHandler.java | 36 - .../src/test/resources/log4j2.xml | 20 - 47 files changed, 3 insertions(+), 5837 deletions(-) delete mode 100644 docs/utilities/sqs_batch.md delete mode 100644 docs/utilities/sqs_large_message_handling.md delete mode 100644 examples/powertools-examples-sqs/README.md delete mode 100644 examples/powertools-examples-sqs/events/event.json delete mode 100644 examples/powertools-examples-sqs/pom.xml delete mode 100644 examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java delete mode 100644 examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java delete mode 100644 examples/powertools-examples-sqs/src/main/resources/log4j2.xml delete mode 100644 examples/powertools-examples-sqs/template.yaml delete mode 100644 powertools-sqs/pom.xml delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java delete mode 100644 powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java delete mode 100644 powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java delete mode 100644 powertools-sqs/src/test/resources/SqsFifoBatchEvent.json delete mode 100644 powertools-sqs/src/test/resources/sampleSqsBatchEvent.json delete mode 100644 powertools-sqs/src/test/resources/sampleSqsBatchEventBatchSize25.json delete mode 100644 powertools-sqs/src/test/resources/threeMessageSqsBatchEvent.json delete mode 100644 powertools-test-suite/pom.xml delete mode 100644 powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java delete mode 100644 powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java delete mode 100644 powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java delete mode 100644 powertools-test-suite/src/test/resources/log4j2.xml diff --git a/README.md b/README.md index 70cfab314..47fb59bd7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Powertools for AWS Lambda (Java) +# Powertools for AWS Lambda (Java) V2 + +**This is pre-release code for Powertools for AWS Lambda (Java) V2! Please check out the `main` branch for the stable release** ![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) diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index c4947a6e8..c0c1cd599 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -6,12 +6,6 @@ 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). - ## Features - Automatically retrieve the content of S3 objects when SQS or SNS messages have been offloaded to S3. 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/examples/pom.xml b/examples/pom.xml index 5d19a20fb..0cd62e326 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -33,7 +33,6 @@ <module>powertools-examples-idempotency</module> <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> - <module>powertools-examples-sqs</module> <module>powertools-examples-batch</module> <module>powertools-examples-validation</module> <module>powertools-examples-cloudformation</module> 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/events/event.json b/examples/powertools-examples-sqs/events/event.json deleted file mode 100644 index 3822fadaa..000000000 --- a/examples/powertools-examples-sqs/events/event.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "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" - } - } - \ No newline at end of file diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml deleted file mode 100644 index aeffcac40..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.119</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/src/main/resources/log4j2.xml b/examples/powertools-examples-sqs/src/main/resources/log4j2.xml deleted file mode 100644 index e1fd14cea..000000000 --- a/examples/powertools-examples-sqs/src/main/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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> \ 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/mkdocs.yml b/mkdocs.yml index d54ece508..62d8d75ce 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,9 +18,6 @@ nav: - 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 diff --git a/pom.xml b/pom.xml index 8c9e540f5..bebb2493f 100644 --- a/pom.xml +++ b/pom.xml @@ -45,11 +45,9 @@ <module>powertools-serialization</module> <module>powertools-logging</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> 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/exception/SkippedMessageDueToFailedBatchException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java deleted file mode 100644 index fbb4289d8..000000000 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java +++ /dev/null @@ -1,26 +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.exception; - -/** - * Indicates that a message has been skipped due to the batch it is - * within failing. - */ -public class SkippedMessageDueToFailedBatchException extends Exception { - - public SkippedMessageDueToFailedBatchException() { - } - -} 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/SampleSqsHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java deleted file mode 100644 index 557aa214d..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java +++ /dev/null @@ -1,26 +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 com.amazonaws.services.lambda.runtime.events.SQSEvent; - -public class SampleSqsHandler implements SqsMessageHandler<Object> { - private int counter; - - @Override - public String process(SQSEvent.SQSMessage message) { - return String.valueOf(counter++); - } -} 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/LambdaHandlerApiGateway.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java deleted file mode 100644 index 3bad9644f..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java +++ /dev/null @@ -1,32 +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 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; - -public class LambdaHandlerApiGateway implements RequestHandler<APIGatewayProxyRequestEvent, String> { - - @Override - @SqsLargeMessage - @SqsBatch(value = SampleSqsHandler.class) - public String handleRequest(APIGatewayProxyRequestEvent sqsEvent, Context context) { - return sqsEvent.getBody(); - } -} 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/SqsMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java deleted file mode 100644 index de096679f..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java +++ /dev/null @@ -1,29 +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 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> { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); - } -} 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/handlers/SqsNoDeleteMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java deleted file mode 100644 index e96dc5581..000000000 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java +++ /dev/null @@ -1,29 +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 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 SqsNoDeleteMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - @SqsLargeMessage(deletePayloads = false) - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); - } -} \ No newline at end of file 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/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java deleted file mode 100644 index 5592b1fd3..000000000 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java +++ /dev/null @@ -1,31 +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.handler; - -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.logging.Logging; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; - -public class LoggingOrderMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - @SqsLargeMessage - @Logging(logEvent = true) - public String handleRequest(SQSEvent sqsEvent, Context context) { - return sqsEvent.getRecords().get(0).getBody(); - } -} diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java deleted file mode 100644 index 4a60d0949..000000000 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.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.testsuite.handler; - -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.util.Map; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.tracing.Tracing; - -public class TracingLoggingStreamMessageHandler implements RequestStreamHandler { - - @Logging(logEvent = true) - @Tracing - @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(output, mapper.readValue(input, Map.class)); - } -} 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 From bd03d76cf8e294dd5feda0e98835a9de1623ee13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 14:11:50 +0200 Subject: [PATCH 0418/1008] build(deps): bump aws.sdk.version from 2.20.119 to 2.20.120 (#1349) Bumps `aws.sdk.version` from 2.20.119 to 2.20.120. Updates `software.amazon.awssdk:bom` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:http-client-spi` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.120 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.120 Updates `software.amazon.awssdk:s3` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:dynamodb` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:lambda` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.120 Updates `software.amazon.awssdk:cloudwatch` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:xray` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:cloudformation` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:sts` from 2.20.119 to 2.20.120 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 35d5e340c..ed1c37289 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.119</aws.sdk.version> + <aws.sdk.version>2.20.120</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index aeffcac40..9ff5f81c0 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.119</version> + <version>2.20.120</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 8c9e540f5..5253f8f19 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.119</aws.sdk.version> + <aws.sdk.version>2.20.120</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c5725d1a159a42bec54e05b583dae024c3e60e77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 14:18:55 +0200 Subject: [PATCH 0419/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1350) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.89.0 to 2.90.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.89.0...v2.90.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index f3194c163..7869586e3 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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.89.0</cdk.version> + <cdk.version>2.90.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From 23261c7cf82c57778317b57142e89ef4cb08c737 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 7 Aug 2023 18:39:26 +0100 Subject: [PATCH 0420/1008] v2: fix version (#1348) --- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 0cd62e326..aca250dae 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index d3c4bc49b..10e15d532 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 35d5e340c..c9fe249b3 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/pom.xml index a5d854d6b..d402ce09c 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index f24b2ffc4..38682e164 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index c4631fd05..1c7d177f4 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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.0.0-SNAPSHOT</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 4974842dd..0ec05cd82 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.0.0-SNAPSHOT</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1c7e33de0..b3947513f 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.0.0-SNAPSHOT</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/pom.xml b/pom.xml index bebb2493f..7cdf8a71f 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 9e25dabd8..593b22444 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <build> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index a122e7ac8..336a3f94c 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 78cd735b9..c244f70fc 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 6e82c7aec..49b4b5a20 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.17.0-SNAPSHOT</lambda.powertools.version> + <lambda.powertools.version>2.0.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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index f3194c163..e8a3273b1 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 2e68ea563..6b9e46a4a 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index a9ded60c5..19793d616 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 83650fcde..e13a88e57 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 7b2e99045..0681ed000 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index fab72a9b7..367996e9c 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 2a57f21e3..c34cddef1 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index b5de90f7b..5f397aa9f 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index daec3aa99..1b939f46f 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 28b2f1f68fb016287a657774f7174d562bf7e98a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:16:59 +0200 Subject: [PATCH 0421/1008] build(deps): bump aws.sdk.version from 2.20.120 to 2.20.121 (#1351) Bumps `aws.sdk.version` from 2.20.120 to 2.20.121. Updates `software.amazon.awssdk:bom` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:http-client-spi` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.121 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.121 Updates `software.amazon.awssdk:s3` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:dynamodb` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:lambda` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.121 Updates `software.amazon.awssdk:cloudwatch` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:xray` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:cloudformation` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:sts` from 2.20.120 to 2.20.121 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ed1c37289..987299ecf 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.120</aws.sdk.version> + <aws.sdk.version>2.20.121</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 9ff5f81c0..7e4631d90 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.120</version> + <version>2.20.121</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 5253f8f19..e322fc048 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.120</aws.sdk.version> + <aws.sdk.version>2.20.121</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 594b7885145d702f44c29b38aa3de9a44142f981 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:18:07 +0200 Subject: [PATCH 0422/1008] build(deps): bump aws.sdk.version from 2.20.121 to 2.20.122 (#1354) Bumps `aws.sdk.version` from 2.20.121 to 2.20.122. Updates `software.amazon.awssdk:bom` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:http-client-spi` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.122 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.122 Updates `software.amazon.awssdk:s3` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:dynamodb` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:lambda` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.122 Updates `software.amazon.awssdk:cloudwatch` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:xray` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:cloudformation` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:sts` from 2.20.121 to 2.20.122 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 987299ecf..1da9b4033 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.121</aws.sdk.version> + <aws.sdk.version>2.20.122</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 7e4631d90..c88e15c97 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.121</version> + <version>2.20.122</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index e322fc048..849d94382 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.121</aws.sdk.version> + <aws.sdk.version>2.20.122</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From bc71586d778c331b8e6d91130e68f97e5db3577a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 14:00:12 +0200 Subject: [PATCH 0423/1008] build(deps): bump aws.sdk.version from 2.20.122 to 2.20.123 (#1355) Bumps `aws.sdk.version` from 2.20.122 to 2.20.123. Updates `software.amazon.awssdk:bom` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:http-client-spi` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.123 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.123 Updates `software.amazon.awssdk:s3` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:dynamodb` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:lambda` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.123 Updates `software.amazon.awssdk:cloudwatch` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:xray` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:cloudformation` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:sts` from 2.20.122 to 2.20.123 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 1da9b4033..e2b420a4b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.122</aws.sdk.version> + <aws.sdk.version>2.20.123</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index c88e15c97..675f78708 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.122</version> + <version>2.20.123</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 849d94382..7e96a580c 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.122</aws.sdk.version> + <aws.sdk.version>2.20.123</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From e8cd87817499d5da08a24858fd6c4495ac5eda7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:25:13 +0200 Subject: [PATCH 0424/1008] build(deps): bump aws.sdk.version from 2.20.123 to 2.20.124 (#1356) Bumps `aws.sdk.version` from 2.20.123 to 2.20.124. Updates `software.amazon.awssdk:bom` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:http-client-spi` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.124 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.124 Updates `software.amazon.awssdk:s3` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:dynamodb` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:lambda` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.124 Updates `software.amazon.awssdk:cloudwatch` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:xray` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:cloudformation` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:sts` from 2.20.123 to 2.20.124 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index e2b420a4b..582a77982 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.123</aws.sdk.version> + <aws.sdk.version>2.20.124</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 675f78708..020167a8c 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.123</version> + <version>2.20.124</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 7e96a580c..22c37c43b 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.123</aws.sdk.version> + <aws.sdk.version>2.20.124</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From ae58dd6f197642735df78124f6045fbfefad7615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:29:24 +0200 Subject: [PATCH 0425/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1357) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.90.0 to 2.91.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.90.0...v2.91.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 7869586e3..6fa7f4375 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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.90.0</cdk.version> + <cdk.version>2.91.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From ef11a64df17ee1cbf736aacbd308c9f191d21a8c Mon Sep 17 00:00:00 2001 From: Alexey Soshin <alexey.soshin@gmail.com> Date: Mon, 14 Aug 2023 11:13:08 +0100 Subject: [PATCH 0426/1008] docs: Adding CDK example (#1321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move the current example, which is for SAM, under /sam directory * Create a new directory for CDK * Add CDK Example as a Maven module * CDK Stack stub * Restructure CDK application info Infra and App projects * Define the build of the project inside the CDK stack * Use default account and region * Add example of setting environment variables in CDK * Remove threads from the examples, as this should be covered by documentation * Add general README for the project * Add specific README for SAM * Add specific README for CDK * Use Java11 syntax for CDK * Refactor the code for clarity * Fix imports * Add outputs example * Add test for the stack * Update examples/powertools-examples-core/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update examples/powertools-examples-core/cdk/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Remove unnecessary .gitignore * Mixed log level and sample rate * Combine .gitignore files * Remove `cdk ls`, since there's just a single stack * Remove SAM mentions from the CDK readme * Replace "architecture" with "tool" while talking about SAM/CDK * Update examples/powertools-examples-core/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update examples/powertools-examples-core/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Reformat imports * Don't include version number in the HelloWorld jar * Update examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Add Streaming example as well * Update examples/powertools-examples-core/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/.gitignore Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/.gitignore Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/cdk/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/sam/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Exclude examples from duplicate code scan https://github.com/aws-powertools/powertools-lambda-java/pull/1317/files * Fix broken link to the events file * Trim cdk.json * Trim cdk.json * Downgrade code to Java 8 * Correct README to point to powertools-core-idempotency for a quick start * Add the missing license to the new Java files * Reformat code --------- Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- examples/.gitignore | 2 + examples/README.md | 10 +- examples/pom.xml | 4 +- examples/powertools-examples-core/.gitignore | 0 examples/powertools-examples-core/README.md | 27 +-- .../powertools-examples-core/cdk/README.md | 36 ++++ .../{ => cdk/app}/events/event.json | 0 .../powertools-examples-core/cdk/app/pom.xml | 202 ++++++++++++++++++ .../app}/src/main/java/helloworld/App.java | 26 +-- .../src/main/java/helloworld/AppStream.java | 0 .../app}/src/main/resources/log4j2.xml | 0 .../src/test/java/helloworld/AppTest.java | 0 .../cdk/infra/cdk.json | 36 ++++ .../cdk/infra/pom.xml | 55 +++++ .../cdk/infra/src/main/java/cdk/CdkApp.java | 53 +++++ .../cdk/infra/src/main/java/cdk/CdkStack.java | 144 +++++++++++++ .../infra/src/test/java/cdk/CdkStackTest.java | 48 +++++ .../powertools-examples-core/sam/README.md | 24 +++ .../sam/events/event.json | 63 ++++++ .../{ => sam}/pom.xml | 2 +- .../sam/src/main/java/helloworld/App.java | 107 ++++++++++ .../src/main/java/helloworld/AppStream.java | 38 ++++ .../sam/src/main/resources/log4j2.xml | 16 ++ .../sam/src/test/java/helloworld/AppTest.java | 59 +++++ .../{ => sam}/template.yaml | 0 .../src/main/java/helloworld/App.java | 3 +- 26 files changed, 906 insertions(+), 49 deletions(-) delete mode 100644 examples/powertools-examples-core/.gitignore create mode 100644 examples/powertools-examples-core/cdk/README.md rename examples/powertools-examples-core/{ => cdk/app}/events/event.json (100%) create mode 100644 examples/powertools-examples-core/cdk/app/pom.xml rename examples/powertools-examples-core/{ => cdk/app}/src/main/java/helloworld/App.java (82%) rename examples/powertools-examples-core/{ => cdk/app}/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => cdk/app}/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => cdk/app}/src/test/java/helloworld/AppTest.java (100%) create mode 100644 examples/powertools-examples-core/cdk/infra/cdk.json create mode 100644 examples/powertools-examples-core/cdk/infra/pom.xml create mode 100644 examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java create mode 100644 examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java create mode 100644 examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java create mode 100644 examples/powertools-examples-core/sam/README.md create mode 100644 examples/powertools-examples-core/sam/events/event.json rename examples/powertools-examples-core/{ => sam}/pom.xml (99%) create mode 100644 examples/powertools-examples-core/sam/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/sam/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java rename examples/powertools-examples-core/{ => sam}/template.yaml (100%) diff --git a/examples/.gitignore b/examples/.gitignore index 79e044a40..892320d3b 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,2 +1,4 @@ dependency-reduced-pom.xml .aws-sam +cdk.out +.m2 diff --git a/examples/README.md b/examples/README.md index 9b76faa82..0744c2bb1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,9 @@ Each example can be copied from its subdirectory and used independently of the r ## Examples -* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules +* [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-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads @@ -34,8 +36,8 @@ 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-core module -$ cd powertools-examples-core +# Switch to the directory containing an example for the powertools-idempotency module +$ cd powertools-examples-idempotency # Build and deploy the example $ sam build @@ -52,6 +54,8 @@ 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) + ### External examples You can find more examples in the https://github.com/aws/aws-sam-cli-app-templates project: diff --git a/examples/pom.xml b/examples/pom.xml index 5d19a20fb..9689e8d71 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,7 +29,9 @@ </description> <modules> - <module>powertools-examples-core</module> + <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> diff --git a/examples/powertools-examples-core/.gitignore b/examples/powertools-examples-core/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index a47d0d26c..f11982477 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -5,17 +5,17 @@ This project demonstrates the Lambda for Powertools Java module - including [tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and [metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). -It is made up of the following: +We provide examples for the following infrastructure-as-code tools: +* [AWS SAM](sam/) +* [AWS CDK](cdk/) -- [App.java](src/main/java/helloworld/App.java) - Code for the application's Lambda function. -- [events](events) - Invocation events that you can use to invoke the function. -- [AppTests.java](src/test/java/helloworld/AppTest.java) - Unit tests for the application code. -- [template.yaml](template.yaml) - A template that defines the application's AWS resources. +For each of the tools, the example application is the same, and consists of the following files: -## Deploy the sample application +- [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. -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 files and deployment process for each tool are described in corresponding README files. ## Test the application @@ -35,13 +35,4 @@ different function calls within 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](src/main/java/helloworld/App.java). - -You can also 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 +[App.java](sam/src/main/java/helloworld/App.java). diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core/cdk/README.md new file mode 100644 index 000000000..f15a24168 --- /dev/null +++ b/examples/powertools-examples-core/cdk/README.md @@ -0,0 +1,36 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with CDK + +This project demonstrates the Lambda for Powertools Java module deployed using [Cloud Development Kit](https://aws.amazon.com/cdk/). + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## 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 + - [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. + +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 + +The minimum to deploy the app should be +```bash +cdk deploy +``` + +If you're running CDK for the first time, you'll need to first run the bootstrap command: +```bash +cdk bootstrap +``` + +## Useful commands + +* `mvn package` compile and run tests +* `cdk synth` emits the synthesized CloudFormation template +* `cdk deploy` deploy this stack to your default AWS account/region +* `cdk diff` compare deployed stack with current state +* `cdk docs` open CDK documentation \ No newline at end of file diff --git a/examples/powertools-examples-core/events/event.json b/examples/powertools-examples-core/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/events/event.json rename to examples/powertools-examples-core/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml new file mode 100644 index 000000000..a4359e223 --- /dev/null +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -0,0 +1,202 @@ +<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/src/main/java/helloworld/App.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java similarity index 82% rename from examples/powertools-examples-core/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java index a1ea9a9e3..988da2a73 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java @@ -17,14 +17,11 @@ 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 static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; 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.AWSXRay; -import com.amazonaws.xray.entities.Entity; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -83,38 +80,17 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv log.info(output); }); - threadOption1(); - - threadOption2(); - log.info("After output"); return response .withStatusCode(200) .withBody(output); - } catch (IOException | InterruptedException e) { + } catch (IOException e) { return response .withBody("{}") .withStatusCode(500); } } - private void threadOption1() throws InterruptedException { - final Entity traceEntity = AWSXRay.getTraceEntity(); - assert traceEntity != null; - traceEntity.run(new Thread(this::log)); - } - - private void threadOption2() throws InterruptedException { - Entity traceEntity = AWSXRay.getTraceEntity(); - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> - { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); - anotherThread.start(); - anotherThread.join(); - } - @Tracing private void log() { log.info("inside threaded logging for function"); diff --git a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/src/main/resources/log4j2.xml b/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/cdk/infra/cdk.json b/examples/powertools-examples-core/cdk/infra/cdk.json new file mode 100644 index 000000000..e24ee7b04 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/cdk.json @@ -0,0 +1,36 @@ +{ + "app": "mvn -e -q compile exec:java", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "target", + "pom.xml", + "src/test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true + } +} diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml new file mode 100644 index 000000000..6431de073 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <artifactId>cdk</artifactId> + <version>1.17.0-SNAPSHOT</version> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <cdk.version>2.88.0</cdk.version> + <constructs.version>[10.0.0,11.0.0)</constructs.version> + <junit.version>5.7.1</junit.version> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.0.0</version> + <configuration> + <mainClass>cdk.CdkApp</mainClass> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <!-- AWS Cloud Development Kit --> + <dependency> + <groupId>software.amazon.awscdk</groupId> + <artifactId>aws-cdk-lib</artifactId> + <version>${cdk.version}</version> + </dependency> + <dependency> + <groupId>software.constructs</groupId> + <artifactId>constructs</artifactId> + <version>${constructs.version}</version> + </dependency> + + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java new file mode 100644 index 000000000..d564eb9a0 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.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 cdk; + +import software.amazon.awscdk.App; +import software.amazon.awscdk.StackProps; + +public class CdkApp { + public static void main(final String[] args) { + App app = new App(); + + new CdkStack(app, "CdkStack", StackProps.builder() + // If you don't specify 'env', this stack will be environment-agnostic. + // Account/Region-dependent features and context lookups will not work, + // but a single synthesized template can be deployed anywhere. + + // Uncomment the next block to specialize this stack for the AWS Account + // and Region that are implied by the current CLI configuration. + /* + .env(Environment.builder() + .account(System.getenv("CDK_DEFAULT_ACCOUNT")) + .region(System.getenv("CDK_DEFAULT_REGION")) + .build()) + */ + + + // Uncomment the next block if you know exactly what Account and Region you + // want to deploy the stack to. + /* + .env(Environment.builder() + .account("1234567890") + .region("region") + .build()) + */ + + // For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html + .build()); + + app.synth(); + } +} diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java new file mode 100644 index 000000000..8e6b44112 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.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 cdk; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import software.amazon.awscdk.BundlingOptions; +import software.amazon.awscdk.CfnOutput; +import software.amazon.awscdk.Duration; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.StackProps; +import software.amazon.awscdk.services.apigateway.LambdaIntegration; +import software.amazon.awscdk.services.apigateway.RestApi; +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.Tracing; +import software.amazon.awscdk.services.s3.assets.AssetOptions; +import software.constructs.Construct; + +/** + * Defines a stack that consists of a single Java Lambda function and an API Gateway + */ +public class CdkStack extends Stack { + private static final String SHELL_COMMAND = "/bin/sh"; + private static final String MAVEN_PACKAGE = "mvn package"; + private static final String COPY_OUTPUT = "cp /asset-input/target/helloworld-lambda.jar /asset-output/"; + + public CdkStack(final Construct scope, final String id) { + this(scope, id, null); + } + + public CdkStack(final Construct scope, final String id, final StackProps props) { + super(scope, id, props); + + Function helloWorldFunction = createHelloWorldFunction(); + Function helloWorldStreamFunction = createHelloWorldStreamFunction(); + RestApi restApi = createHelloWorldApi(); + + restApi.getRoot().resourceForPath("/hello") + .addMethod("GET", LambdaIntegration.Builder.create(helloWorldFunction) + .build()); + + restApi.getRoot().resourceForPath("/hellostream") + .addMethod("GET", LambdaIntegration.Builder.create(helloWorldStreamFunction) + .build()); + + outputApiUrl(restApi); + } + + private static List<String> createFunctionPackageInstructions() { + // CDK will use this command to package your Java Lambda + return Arrays.asList( + SHELL_COMMAND, + "-c", + MAVEN_PACKAGE + " && " + + COPY_OUTPUT + ); + } + + /** + * Adds API URL to the outputs + * + * @param restApi + */ + private void outputApiUrl(RestApi restApi) { + CfnOutput.Builder.create(this, "HelloWorldApiUrl") + .description("API Gateway endpoint URL for Prod stage for Hello World function") + .value(restApi.getUrl() + "hello").build(); + } + + // Method to create the Lambda function + private Function createHelloWorldFunction() { + List<String> functionPackageInstructions = createFunctionPackageInstructions(); + + Map<String, String> environment = new HashMap<>(); + environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); + environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.1"); + environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); + environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); + + return Function.Builder.create(this, "HelloWorldFunction") + .runtime(Runtime.JAVA_11) + .memorySize(512) + .timeout(Duration.seconds(20)) + .tracing(Tracing.ACTIVE) + .code(Code.fromAsset("../app/", AssetOptions.builder() + .bundling(BundlingOptions.builder() + .image(Runtime.JAVA_11.getBundlingImage()) + .command(functionPackageInstructions) + .build()) + .build())) + .handler("helloworld.App") + .environment(environment) + .build(); + } + + private Function createHelloWorldStreamFunction() { + List<String> functionPackageInstructions = createFunctionPackageInstructions(); + + Map<String, String> environment = new HashMap<>(); + environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); + environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.7"); + environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); + environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); + environment.put("POWERTOOLS_SERVICE_NAME", "hello"); + + return Function.Builder.create(this, "HelloWorldStreamFunction") + .runtime(Runtime.JAVA_11) + .memorySize(512) + .timeout(Duration.seconds(20)) + .tracing(Tracing.ACTIVE) + .code(Code.fromAsset("../app/", AssetOptions.builder() + .bundling(BundlingOptions.builder() + .image(Runtime.JAVA_11.getBundlingImage()) + .command(functionPackageInstructions) + .build()) + .build())) + .handler("helloworld.AppStream") + .environment(environment) + .build(); + } + + // Method to create the REST API + private RestApi createHelloWorldApi() { + return RestApi.Builder.create(this, "HelloWorldApi") + .description("API Gateway endpoint URL for Prod stage for Hello World function") + .build(); + } +} 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 new file mode 100644 index 000000000..29cb15545 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.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 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/README.md b/examples/powertools-examples-core/sam/README.md new file mode 100644 index 000000000..7a4279ae3 --- /dev/null +++ b/examples/powertools-examples-core/sam/README.md @@ -0,0 +1,24 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with SAM + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/). + +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) + +## 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/sam/events/event.json b/examples/powertools-examples-core/sam/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-core/sam/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/sam/pom.xml similarity index 99% rename from examples/powertools-examples-core/pom.xml rename to examples/powertools-examples-core/sam/pom.xml index a5d854d6b..bc3667b4f 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda.examples</groupId> <version>1.17.0-SNAPSHOT</version> - <artifactId>powertools-examples-core</artifactId> + <artifactId>powertools-examples-core-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Core</name> diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java new file mode 100644 index 000000000..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e1fd14cea --- /dev/null +++ b/examples/powertools-examples-core/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> \ No newline at end of file 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 new file mode 100644 index 000000000..70dad8d71 --- /dev/null +++ b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.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 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/template.yaml b/examples/powertools-examples-core/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/template.yaml rename to examples/powertools-examples-core/sam/template.yaml diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index ac2c7ef1b..72fa621ad 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -22,6 +22,7 @@ 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; @@ -113,7 +114,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv */ private String getPageContents(String address) throws IOException { URL url = new URL(address); - try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { return br.lines().collect(Collectors.joining(System.lineSeparator())); } } From 6f05396eeb7f87836f99798624e8e06e771ddc88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:52:54 +0200 Subject: [PATCH 0427/1008] build(deps-dev): bump org.yaml:snakeyaml from 2.0 to 2.1 (#1344) Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 2.0 to 2.1. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.1..snakeyaml-2.0) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 6fa7f4375..534b65d3b 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -162,7 +162,7 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>2.0</version> + <version>2.1</version> <scope>test</scope> </dependency> <dependency> From f03832b5957f15b1c55cf0d1d7124d5d74faff57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:36:27 +0200 Subject: [PATCH 0428/1008] build(deps): bump org.apache.maven.plugins:maven-compiler-plugin (#1360) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.1 to 3.11.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 6431de073..f1f28c397 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -16,7 +16,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.8.1</version> + <version>3.11.0</version> <configuration> <source>1.8</source> <target>1.8</target> From 78e8972127defde5a2fcbcc01b51e10165fd12a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 10:43:32 +0200 Subject: [PATCH 0429/1008] build(deps-dev): bump org.junit.jupiter:junit-jupiter (#1362) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.7.1 to 5.10.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.1...r5.10.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index f1f28c397..c96b4deb2 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -9,7 +9,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.88.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.7.1</junit.version> + <junit.version>5.10.0</junit.version> </properties> <build> <plugins> From 3ebd5cc58423f4e5ed5fa916fdd2dcf3a27fcde9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 10:44:22 +0200 Subject: [PATCH 0430/1008] build(deps): bump aws.sdk.version from 2.20.124 to 2.20.125 (#1361) Bumps `aws.sdk.version` from 2.20.124 to 2.20.125. Updates `software.amazon.awssdk:bom` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:http-client-spi` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.125 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.125 Updates `software.amazon.awssdk:s3` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:dynamodb` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:lambda` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.125 Updates `software.amazon.awssdk:cloudwatch` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:xray` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:cloudformation` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:sts` from 2.20.124 to 2.20.125 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 582a77982..48ac69ca4 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.124</aws.sdk.version> + <aws.sdk.version>2.20.125</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 020167a8c..0b43cc612 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.124</version> + <version>2.20.125</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 22c37c43b..0926e8211 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.124</aws.sdk.version> + <aws.sdk.version>2.20.125</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From adb2a7eecfed1b755466e908958526b5b16fdd56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 13:31:46 +0200 Subject: [PATCH 0431/1008] build(deps): bump org.codehaus.mojo:exec-maven-plugin (#1366) Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/exec-maven-plugin-3.0.0...exec-maven-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index c96b4deb2..4baa73066 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -25,7 +25,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.0.0</version> + <version>3.1.0</version> <configuration> <mainClass>cdk.CdkApp</mainClass> </configuration> From aa40708e9e2f22a962d9b70d181a484aad61d4fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 14:22:28 +0200 Subject: [PATCH 0432/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1368) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.88.0 to 2.91.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.88.0...v2.91.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 4baa73066..3dc7adb6a 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>1.17.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.88.0</cdk.version> + <cdk.version>2.91.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> From bca90c921710a1ce586cb594f5a5618bafa7e407 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 14:22:44 +0200 Subject: [PATCH 0433/1008] build(deps): bump aws.sdk.version from 2.20.125 to 2.20.126 (#1367) Bumps `aws.sdk.version` from 2.20.125 to 2.20.126. Updates `software.amazon.awssdk:bom` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:http-client-spi` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.126 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.126 Updates `software.amazon.awssdk:s3` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:dynamodb` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:lambda` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.126 Updates `software.amazon.awssdk:cloudwatch` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:xray` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:cloudformation` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:sts` from 2.20.125 to 2.20.126 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 48ac69ca4..ad1c2eb51 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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.125</aws.sdk.version> + <aws.sdk.version>2.20.126</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0b43cc612..e5156adee 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.125</version> + <version>2.20.126</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 0926e8211..d75a731b1 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.125</aws.sdk.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> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From be284e24a2c1ca9a35e079a8f2728400119d5040 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:57:03 +0200 Subject: [PATCH 0434/1008] build(deps): bump aws.sdk.version from 2.20.126 to 2.20.127 (#1372) Bumps `aws.sdk.version` from 2.20.126 to 2.20.127. Updates `software.amazon.awssdk:bom` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:http-client-spi` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.127 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.127 Updates `software.amazon.awssdk:s3` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:dynamodb` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:lambda` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.127 Updates `software.amazon.awssdk:cloudwatch` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:xray` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:cloudformation` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:sts` from 2.20.126 to 2.20.127 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ad1c2eb51..8ce21299d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <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> + <aws.sdk.version>2.20.127</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index e5156adee..e50ab2f3f 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.126</version> + <version>2.20.127</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index d75a731b1..ec0b5ae21 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.sdk.version>2.20.127</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 424f8883cf01decb0b6ae4ac13ab31a5bb448c77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:22:12 +0200 Subject: [PATCH 0435/1008] build(deps): bump aws.sdk.version from 2.20.109 to 2.20.128 (#1377) Bumps `aws.sdk.version` from 2.20.109 to 2.20.128. Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:sdk-core` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.109 to 2.20.128 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index d3c4bc49b..2096774b5 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -15,7 +15,7 @@ <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> + <sdk.version>2.20.128</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index e50ab2f3f..bcbab5472 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.127</version> + <version>2.20.128</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 29f71ddb4373e24549916b1f06a715cfe8d674e2 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:32:45 +0100 Subject: [PATCH 0436/1008] fix: Roll log4j shade transformer forwards (#1376) * Roll log4j shade transformer forwards * missed one --- examples/powertools-examples-batch/pom.xml | 10 ++++------ .../powertools-examples-cloudformation/pom.xml | 10 ++++------ examples/powertools-examples-core/cdk/app/pom.xml | 10 ++++------ examples/powertools-examples-core/sam/pom.xml | 10 ++++------ examples/powertools-examples-idempotency/pom.xml | 10 ++++------ examples/powertools-examples-sqs/pom.xml | 10 ++++------ powertools-e2e-tests/handlers/pom.xml | 14 ++++++-------- 7 files changed, 30 insertions(+), 44 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 2096774b5..cb7b8242c 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -107,18 +107,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8ce21299d..c6c12e4d9 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -132,18 +132,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index a4359e223..7b57bd55d 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -107,18 +107,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index bc3667b4f..f1bffb896 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -106,18 +106,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index f24b2ffc4..533f6acfd 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -169,18 +169,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index bcbab5472..b8b6c2578 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -105,18 +105,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 6e82c7aec..dcf8908e7 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -120,19 +120,17 @@ </goals> <configuration> <transformers> - <transformer - implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"> - </transformer> + <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> - </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.1.0</version> + </dependency> </dependencies> </plugin> <plugin> From a0ea4c3d9d7b324d9f95a74a593e975869ff1ded Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:14:52 +0200 Subject: [PATCH 0437/1008] build(deps): bump com.amazonaws:aws-lambda-java-core from 1.2.2 to 1.2.3 (#1379) Bumps [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs) from 1.2.2 to 1.2.3. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/app/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index cb7b8242c..418da8fe7 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -37,7 +37,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c6c12e4d9..f3b14648f 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> <aws.sdk.version>2.20.127</aws.sdk.version> </properties> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index 7b57bd55d..a1aba1b8d 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index f1bffb896..4d90bff94 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 533f6acfd..c6afdcbe2 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -48,7 +48,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index c4631fd05..dc3395069 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 4974842dd..459d7a7b2 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index b8b6c2578..99815d5c0 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1c7e33de0..f916930d5 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <!-- Test dependencies --> diff --git a/pom.xml b/pom.xml index ec0b5ae21..0704c9057 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <lambda.core.version>1.2.2</lambda.core.version> + <lambda.core.version>1.2.3</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> From e7fb00c4727ae0d26fcca71238ce76d4f87da034 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:15:10 +0200 Subject: [PATCH 0438/1008] build(deps): bump aws.sdk.version from 2.20.127 to 2.20.129 (#1380) Bumps `aws.sdk.version` from 2.20.127 to 2.20.129. Updates `software.amazon.awssdk:bom` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:http-client-spi` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:url-connection-client` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:sqs` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:s3` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:dynamodb` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:lambda` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:kinesis` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:cloudwatch` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:xray` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:cloudformation` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:sts` from 2.20.127 to 2.20.129 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index f3b14648f..d8b2d022d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <maven.deploy.skip>true</maven.deploy.skip> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.127</aws.sdk.version> + <aws.sdk.version>2.20.129</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 99815d5c0..84cbefb2a 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.128</version> + <version>2.20.129</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 0704c9057..5c6d94795 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.127</aws.sdk.version> + <aws.sdk.version>2.20.129</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From cfd45bd04474bc50db08076100b0721171d39696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:22:34 +0200 Subject: [PATCH 0439/1008] chore:Prep release 1.17.0 (#1381) * chore:prep release 1.17.0 * Update changelog --------- Co-authored-by: scottgerring <scottgerring@users.noreply.github.com> Co-authored-by: Scott Gerring <gerrings@amazon.com> --- CHANGELOG.md | 22 +++++++++++++++++++ examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- .../pom.xml | 2 +- .../cdk/infra/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- .../powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- .../powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 27 files changed, 48 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5619f0bbb..dbb90ce3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.17.0] - 2023-08-21 + +### Added +* Feat: Add Batch Processor module in (#1317) by @scottgerring +* Feat: Add SNS+SQS large messages module (#1310) by @jeromevdl + +### Maintenance +* fix: use default credentials provider for all provided SDK clients in (#1303) by @roamingthings +* Chore: Make request for Logger explicitly for current class in (#1307) by @jreijn +* Chore: checkstyle formater & linter in (#1316) by @jeromevdl +* Chore: Add powertools specific user-agent-suffix to the AWS SDK v2 clients by @eldimi in (#1306) +* Chore: Add 'v2' branch to build workflows to prepare for v2 work in (#1341) by @scottgerring +* Deps: Bump third party dependencies to the latest versions. + +### Documentation +* Docs: Add maintainers guide in (#1326) by @scottgerring +* Docs: improve contributing guide in (#1334) by @jeromevdl +* Docs: Improve example documentation in (#1291) by @scottgerring +* Docs: Add discord + sec disclosure links to readme in (#1311) by @scottgerring +* Docs: Add external examples from AWS SAM CLI App Templates in (#1318) by @AlexeySoshin +* Docs: Add CDK example in (#1321) by @AlexeySoshin + ## [1.16.1] - 2023-07-19 * Fix: idempotency timeout bug (#1285) by @scottgerring diff --git a/examples/pom.xml b/examples/pom.xml index 9689e8d71..652a845a5 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 418da8fe7..aea59e873 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d8b2d022d..8a4834cdf 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 3dc7adb6a..6a244a68d 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.91.0</cdk.version> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 4d90bff94..a21d64910 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <artifactId>powertools-examples-core-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index c6afdcbe2..1f194eeb2 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index dc3395069..11d5de24a 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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>1.17.0</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 459d7a7b2..3674c8b10 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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>1.17.0</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 84cbefb2a..277c282a4 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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>1.17.0</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index f916930d5..3fdb55412 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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>1.17.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/pom.xml b/pom.xml index 5c6d94795..3b05583a6 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 9e25dabd8..2243c3097 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <build> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index a122e7ac8..99b4a6fdc 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 78cd735b9..830405376 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index dcf8908e7..9b3636dbd 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.17.0-SNAPSHOT</lambda.powertools.version> + <lambda.powertools.version>1.17.0</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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 534b65d3b..848f7768a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 2e68ea563..3f205b321 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index a9ded60c5..88ace5b4e 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 83650fcde..5e1ee339c 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 7b2e99045..253b2596f 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index fab72a9b7..74055614a 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 2a57f21e3..3dd59ba32 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index c21943fba..1e274011d 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 4a79d2987..4969f171a 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index b5de90f7b..afe4cb570 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index daec3aa99..ad8e192fd 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>1.17.0</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 7b0af2c433e49876e9f18f4c42f08dfddcf603fc Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 21 Aug 2023 09:48:22 +0100 Subject: [PATCH 0440/1008] chore: Fix missing version change pieces (#1382) * Add missing bits * And fixed the fix --- README.md | 6 +++--- mkdocs.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 70cfab314..e120bfb00 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.16.1</version> + <version>1.17.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.16.1</version> + <version>1.17.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.16.1</version> + <version>1.17.0</version> </dependency> ... </dependencies> diff --git a/mkdocs.yml b/mkdocs.yml index d54ece508..0f9c065a7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,7 +88,7 @@ extra_javascript: extra: powertools: - version: 1.16.1 # to update after each release (we do not want snapshot version here) + version: 1.17.0 # to update after each release (we do not want snapshot version here) repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs From 018e3196c60a0b3fd1522c2f1229ab5129ffc000 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:59:38 +0100 Subject: [PATCH 0441/1008] We shouldn't deploy CDK (#1383) --- examples/powertools-examples-core/cdk/infra/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 6a244a68d..9a7850b93 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -10,6 +10,7 @@ <cdk.version>2.91.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> + <maven.deploy.skip>true</maven.deploy.skip> </properties> <build> <plugins> From 1c847957911a2c3a6bd9cdac9c00a1e2552e8915 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 14:04:08 +0200 Subject: [PATCH 0442/1008] build(deps): bump aws.sdk.version from 2.20.129 to 2.20.130 (#1386) Bumps `aws.sdk.version` from 2.20.129 to 2.20.130. Updates `software.amazon.awssdk:bom` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:http-client-spi` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:url-connection-client` from 2.20.128 to 2.20.130 Updates `software.amazon.awssdk:sqs` from 2.20.128 to 2.20.130 Updates `software.amazon.awssdk:s3` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:dynamodb` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:lambda` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:kinesis` from 2.20.128 to 2.20.130 Updates `software.amazon.awssdk:cloudwatch` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:xray` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:cloudformation` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:sts` from 2.20.129 to 2.20.130 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8a4834cdf..86bd29f93 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <maven.deploy.skip>true</maven.deploy.skip> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.129</aws.sdk.version> + <aws.sdk.version>2.20.130</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 277c282a4..f676e1a73 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.129</version> + <version>2.20.130</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 3b05583a6..2bd2f9ad2 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.129</aws.sdk.version> + <aws.sdk.version>2.20.130</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 99982d6cd54cf44b9603f31a0548e292a60a5630 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 21 Aug 2023 13:39:10 +0100 Subject: [PATCH 0443/1008] Fix batch pom (#1385) --- powertools-batch/pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 2243c3097..82d1d57b2 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -9,6 +9,9 @@ <version>1.17.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> From b01e3dfa4f23f5a58bc154208fa473f224e8999e Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 21 Aug 2023 13:55:37 +0100 Subject: [PATCH 0444/1008] Update to snapshot (#1384) --- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 26 files changed, 26 insertions(+), 26 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 652a845a5..948005546 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index aea59e873..dd75b597b 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 86bd29f93..93cf4acc1 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 9a7850b93..d81118cde 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.91.0</cdk.version> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index a21d64910..7350542cd 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-core-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 1f194eeb2..e9337aafc 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 11d5de24a..301247136 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 3674c8b10..18318ff76 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index f676e1a73..5eee08b22 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 3fdb55412..3aa8dad2b 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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</version> + <version>1.18.0-SNAPSHOT</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/pom.xml b/pom.xml index 2bd2f9ad2..d806dcb43 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 82d1d57b2..27bb33a42 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 99b4a6fdc..1347d95a3 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 830405376..a5460031d 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 9b3636dbd..6507e3104 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.17.0</lambda.powertools.version> + <lambda.powertools.version>1.18.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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 848f7768a..e13cb65fc 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 3f205b321..e89df47f7 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 88ace5b4e..aed8cc2e4 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 5e1ee339c..26ad8c243 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 253b2596f..cb3acf110 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 74055614a..f99a3d3cd 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 3dd59ba32..b1f228df7 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 1e274011d..f824f409d 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 4969f171a..80b8635f5 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index afe4cb570..d2f14c3e0 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index ad8e192fd..23841777e 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.17.0</version> + <version>1.18.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 07c31d3e337d2585a08e9d569654440df8149a92 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 21 Aug 2023 16:39:44 +0100 Subject: [PATCH 0445/1008] Fix batch logging (#1387) --- .../main/java/org/demo/batch/kinesis/KinesisBatchHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..b188df501 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 @@ -12,7 +12,7 @@ public class KinesisBatchHandler implements RequestHandler<KinesisEvent, StreamsEventResponse> { - private final static Logger LOGGER = LogManager.getLogger(org.demo.batch.sqs.SqsBatchHandler.class); + private final static Logger LOGGER = LogManager.getLogger(KinesisBatchHandler.class); private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; public KinesisBatchHandler() { From 869192a5756f9a0e54d4c840fd2e79a2bda4a0be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:31:35 +0200 Subject: [PATCH 0446/1008] build(deps): bump aws.sdk.version from 2.20.130 to 2.20.131 (#1389) Bumps `aws.sdk.version` from 2.20.130 to 2.20.131. Updates `software.amazon.awssdk:bom` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:http-client-spi` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:url-connection-client` from 2.20.128 to 2.20.131 Updates `software.amazon.awssdk:sqs` from 2.20.128 to 2.20.131 Updates `software.amazon.awssdk:s3` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:dynamodb` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:lambda` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:kinesis` from 2.20.128 to 2.20.131 Updates `software.amazon.awssdk:cloudwatch` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:xray` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:cloudformation` from 2.20.130 to 2.20.131 Updates `software.amazon.awssdk:sts` from 2.20.130 to 2.20.131 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 93cf4acc1..4c4a53039 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <maven.deploy.skip>true</maven.deploy.skip> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.130</aws.sdk.version> + <aws.sdk.version>2.20.131</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 5eee08b22..8969662a8 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.130</version> + <version>2.20.131</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index d806dcb43..76cdee1f8 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.130</aws.sdk.version> + <aws.sdk.version>2.20.131</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 69eafe6279135d96091281eea6a605246b151037 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:19:22 +0100 Subject: [PATCH 0447/1008] chore: V2 update from main (#1365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(deps): bump aws.sdk.version from 2.20.119 to 2.20.120 (#1349) Bumps `aws.sdk.version` from 2.20.119 to 2.20.120. Updates `software.amazon.awssdk:bom` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:http-client-spi` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.120 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.120 Updates `software.amazon.awssdk:s3` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:dynamodb` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:lambda` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.120 Updates `software.amazon.awssdk:cloudwatch` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:xray` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:cloudformation` from 2.20.119 to 2.20.120 Updates `software.amazon.awssdk:sts` from 2.20.119 to 2.20.120 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1350) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.89.0 to 2.90.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.89.0...v2.90.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.120 to 2.20.121 (#1351) Bumps `aws.sdk.version` from 2.20.120 to 2.20.121. Updates `software.amazon.awssdk:bom` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:http-client-spi` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.121 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.121 Updates `software.amazon.awssdk:s3` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:dynamodb` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:lambda` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.121 Updates `software.amazon.awssdk:cloudwatch` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:xray` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:cloudformation` from 2.20.120 to 2.20.121 Updates `software.amazon.awssdk:sts` from 2.20.120 to 2.20.121 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.121 to 2.20.122 (#1354) Bumps `aws.sdk.version` from 2.20.121 to 2.20.122. Updates `software.amazon.awssdk:bom` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:http-client-spi` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.122 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.122 Updates `software.amazon.awssdk:s3` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:dynamodb` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:lambda` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.122 Updates `software.amazon.awssdk:cloudwatch` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:xray` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:cloudformation` from 2.20.121 to 2.20.122 Updates `software.amazon.awssdk:sts` from 2.20.121 to 2.20.122 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.122 to 2.20.123 (#1355) Bumps `aws.sdk.version` from 2.20.122 to 2.20.123. Updates `software.amazon.awssdk:bom` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:http-client-spi` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.123 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.123 Updates `software.amazon.awssdk:s3` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:dynamodb` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:lambda` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.123 Updates `software.amazon.awssdk:cloudwatch` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:xray` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:cloudformation` from 2.20.122 to 2.20.123 Updates `software.amazon.awssdk:sts` from 2.20.122 to 2.20.123 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.123 to 2.20.124 (#1356) Bumps `aws.sdk.version` from 2.20.123 to 2.20.124. Updates `software.amazon.awssdk:bom` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:http-client-spi` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.124 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.124 Updates `software.amazon.awssdk:s3` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:dynamodb` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:lambda` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.124 Updates `software.amazon.awssdk:cloudwatch` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:xray` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:cloudformation` from 2.20.123 to 2.20.124 Updates `software.amazon.awssdk:sts` from 2.20.123 to 2.20.124 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1357) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.90.0 to 2.91.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.90.0...v2.91.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * docs: Adding CDK example (#1321) * Move the current example, which is for SAM, under /sam directory * Create a new directory for CDK * Add CDK Example as a Maven module * CDK Stack stub * Restructure CDK application info Infra and App projects * Define the build of the project inside the CDK stack * Use default account and region * Add example of setting environment variables in CDK * Remove threads from the examples, as this should be covered by documentation * Add general README for the project * Add specific README for SAM * Add specific README for CDK * Use Java11 syntax for CDK * Refactor the code for clarity * Fix imports * Add outputs example * Add test for the stack * Update examples/powertools-examples-core/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update examples/powertools-examples-core/cdk/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Remove unnecessary .gitignore * Mixed log level and sample rate * Combine .gitignore files * Remove `cdk ls`, since there's just a single stack * Remove SAM mentions from the CDK readme * Replace "architecture" with "tool" while talking about SAM/CDK * Update examples/powertools-examples-core/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update examples/powertools-examples-core/README.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Reformat imports * Don't include version number in the HelloWorld jar * Update examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Add Streaming example as well * Update examples/powertools-examples-core/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/.gitignore Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/.gitignore Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/cdk/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/sam/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Exclude examples from duplicate code scan https://github.com/aws-powertools/powertools-lambda-java/pull/1317/files * Fix broken link to the events file * Trim cdk.json * Trim cdk.json * Downgrade code to Java 8 * Correct README to point to powertools-core-idempotency for a quick start * Add the missing license to the new Java files * Reformat code --------- Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * build(deps-dev): bump org.yaml:snakeyaml from 2.0 to 2.1 (#1344) Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 2.0 to 2.1. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.1..snakeyaml-2.0) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * build(deps): bump org.apache.maven.plugins:maven-compiler-plugin (#1360) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.1 to 3.11.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump org.junit.jupiter:junit-jupiter (#1362) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.7.1 to 5.10.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.1...r5.10.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.124 to 2.20.125 (#1361) Bumps `aws.sdk.version` from 2.20.124 to 2.20.125. Updates `software.amazon.awssdk:bom` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:http-client-spi` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.125 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.125 Updates `software.amazon.awssdk:s3` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:dynamodb` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:lambda` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.125 Updates `software.amazon.awssdk:cloudwatch` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:xray` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:cloudformation` from 2.20.124 to 2.20.125 Updates `software.amazon.awssdk:sts` from 2.20.124 to 2.20.125 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump org.codehaus.mojo:exec-maven-plugin (#1366) Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/exec-maven-plugin-3.0.0...exec-maven-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1368) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.88.0 to 2.91.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.88.0...v2.91.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.125 to 2.20.126 (#1367) Bumps `aws.sdk.version` from 2.20.125 to 2.20.126. Updates `software.amazon.awssdk:bom` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:http-client-spi` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.126 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.126 Updates `software.amazon.awssdk:s3` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:dynamodb` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:lambda` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.126 Updates `software.amazon.awssdk:cloudwatch` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:xray` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:cloudformation` from 2.20.125 to 2.20.126 Updates `software.amazon.awssdk:sts` from 2.20.125 to 2.20.126 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.126 to 2.20.127 (#1372) Bumps `aws.sdk.version` from 2.20.126 to 2.20.127. Updates `software.amazon.awssdk:bom` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:http-client-spi` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.127 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.127 Updates `software.amazon.awssdk:s3` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:dynamodb` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:lambda` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.127 Updates `software.amazon.awssdk:cloudwatch` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:xray` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:cloudformation` from 2.20.126 to 2.20.127 Updates `software.amazon.awssdk:sts` from 2.20.126 to 2.20.127 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.109 to 2.20.128 (#1377) Bumps `aws.sdk.version` from 2.20.109 to 2.20.128. Updates `software.amazon.awssdk:url-connection-client` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:sqs` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:sdk-core` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:kinesis` from 2.20.109 to 2.20.128 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.109 to 2.20.128 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Roll log4j shade transformer forwards (#1376) * Roll log4j shade transformer forwards * missed one * build(deps): bump com.amazonaws:aws-lambda-java-core from 1.2.2 to 1.2.3 (#1379) Bumps [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs) from 1.2.2 to 1.2.3. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump aws.sdk.version from 2.20.127 to 2.20.129 (#1380) Bumps `aws.sdk.version` from 2.20.127 to 2.20.129. Updates `software.amazon.awssdk:bom` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:http-client-spi` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:url-connection-client` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:sqs` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:s3` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:dynamodb` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:lambda` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:kinesis` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:cloudwatch` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:xray` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:cloudformation` from 2.20.127 to 2.20.129 Updates `software.amazon.awssdk:sts` from 2.20.127 to 2.20.129 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore:Prep release 1.17.0 (#1381) * chore:prep release 1.17.0 * Update changelog --------- Co-authored-by: scottgerring <scottgerring@users.noreply.github.com> Co-authored-by: Scott Gerring <gerrings@amazon.com> * chore: Fix missing version change pieces (#1382) * Add missing bits * And fixed the fix * We shouldn't deploy CDK (#1383) * build(deps): bump aws.sdk.version from 2.20.129 to 2.20.130 (#1386) Bumps `aws.sdk.version` from 2.20.129 to 2.20.130. Updates `software.amazon.awssdk:bom` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:http-client-spi` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:url-connection-client` from 2.20.128 to 2.20.130 Updates `software.amazon.awssdk:sqs` from 2.20.128 to 2.20.130 Updates `software.amazon.awssdk:s3` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:dynamodb` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:lambda` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:kinesis` from 2.20.128 to 2.20.130 Updates `software.amazon.awssdk:cloudwatch` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:xray` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:cloudformation` from 2.20.129 to 2.20.130 Updates `software.amazon.awssdk:sts` from 2.20.129 to 2.20.130 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix batch pom (#1385) * Update to snapshot (#1384) * Fix batch logging (#1387) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexey Soshin <alexey.soshin@gmail.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- CHANGELOG.md | 22 ++ README.md | 6 +- examples/.gitignore | 2 + examples/README.md | 10 +- examples/pom.xml | 4 +- examples/powertools-examples-batch/pom.xml | 14 +- .../batch/kinesis/KinesisBatchHandler.java | 2 +- .../pom.xml | 14 +- examples/powertools-examples-core/.gitignore | 0 examples/powertools-examples-core/README.md | 27 +-- .../powertools-examples-core/cdk/README.md | 36 ++++ .../{ => cdk/app}/events/event.json | 0 .../powertools-examples-core/cdk/app/pom.xml | 200 ++++++++++++++++++ .../app}/src/main/java/helloworld/App.java | 26 +-- .../src/main/java/helloworld/AppStream.java | 0 .../app}/src/main/resources/log4j2.xml | 0 .../src/test/java/helloworld/AppTest.java | 0 .../cdk/infra/cdk.json | 36 ++++ .../cdk/infra/pom.xml | 56 +++++ .../cdk/infra/src/main/java/cdk/CdkApp.java | 53 +++++ .../cdk/infra/src/main/java/cdk/CdkStack.java | 144 +++++++++++++ .../infra/src/test/java/cdk/CdkStackTest.java | 48 +++++ .../powertools-examples-core/sam/README.md | 24 +++ .../sam/events/event.json | 63 ++++++ .../{ => sam}/pom.xml | 12 +- .../sam/src/main/java/helloworld/App.java | 107 ++++++++++ .../src/main/java/helloworld/AppStream.java | 38 ++++ .../sam/src/main/resources/log4j2.xml | 16 ++ .../sam/src/test/java/helloworld/AppTest.java | 59 ++++++ .../{ => sam}/template.yaml | 0 .../powertools-examples-idempotency/pom.xml | 12 +- .../src/main/java/helloworld/App.java | 3 +- .../powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- .../powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 4 +- powertools-batch/pom.xml | 3 + powertools-e2e-tests/handlers/pom.xml | 14 +- powertools-e2e-tests/pom.xml | 4 +- 40 files changed, 969 insertions(+), 98 deletions(-) delete mode 100644 examples/powertools-examples-core/.gitignore create mode 100644 examples/powertools-examples-core/cdk/README.md rename examples/powertools-examples-core/{ => cdk/app}/events/event.json (100%) create mode 100644 examples/powertools-examples-core/cdk/app/pom.xml rename examples/powertools-examples-core/{ => cdk/app}/src/main/java/helloworld/App.java (82%) rename examples/powertools-examples-core/{ => cdk/app}/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => cdk/app}/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => cdk/app}/src/test/java/helloworld/AppTest.java (100%) create mode 100644 examples/powertools-examples-core/cdk/infra/cdk.json create mode 100644 examples/powertools-examples-core/cdk/infra/pom.xml create mode 100644 examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java create mode 100644 examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java create mode 100644 examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java create mode 100644 examples/powertools-examples-core/sam/README.md create mode 100644 examples/powertools-examples-core/sam/events/event.json rename examples/powertools-examples-core/{ => sam}/pom.xml (94%) create mode 100644 examples/powertools-examples-core/sam/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/sam/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java rename examples/powertools-examples-core/{ => sam}/template.yaml (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5619f0bbb..dbb90ce3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.17.0] - 2023-08-21 + +### Added +* Feat: Add Batch Processor module in (#1317) by @scottgerring +* Feat: Add SNS+SQS large messages module (#1310) by @jeromevdl + +### Maintenance +* fix: use default credentials provider for all provided SDK clients in (#1303) by @roamingthings +* Chore: Make request for Logger explicitly for current class in (#1307) by @jreijn +* Chore: checkstyle formater & linter in (#1316) by @jeromevdl +* Chore: Add powertools specific user-agent-suffix to the AWS SDK v2 clients by @eldimi in (#1306) +* Chore: Add 'v2' branch to build workflows to prepare for v2 work in (#1341) by @scottgerring +* Deps: Bump third party dependencies to the latest versions. + +### Documentation +* Docs: Add maintainers guide in (#1326) by @scottgerring +* Docs: improve contributing guide in (#1334) by @jeromevdl +* Docs: Improve example documentation in (#1291) by @scottgerring +* Docs: Add discord + sec disclosure links to readme in (#1311) by @scottgerring +* Docs: Add external examples from AWS SAM CLI App Templates in (#1318) by @AlexeySoshin +* Docs: Add CDK example in (#1321) by @AlexeySoshin + ## [1.16.1] - 2023-07-19 * Fix: idempotency timeout bug (#1285) by @scottgerring diff --git a/README.md b/README.md index 47fb59bd7..76f8b2e4b 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.16.1</version> + <version>1.17.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.16.1</version> + <version>1.17.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.16.1</version> + <version>1.17.0</version> </dependency> ... </dependencies> diff --git a/examples/.gitignore b/examples/.gitignore index 79e044a40..892320d3b 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,2 +1,4 @@ dependency-reduced-pom.xml .aws-sam +cdk.out +.m2 diff --git a/examples/README.md b/examples/README.md index 9b76faa82..0744c2bb1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,9 @@ Each example can be copied from its subdirectory and used independently of the r ## Examples -* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules +* [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-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads @@ -34,8 +36,8 @@ 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-core module -$ cd powertools-examples-core +# Switch to the directory containing an example for the powertools-idempotency module +$ cd powertools-examples-idempotency # Build and deploy the example $ sam build @@ -52,6 +54,8 @@ 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) + ### External examples You can find more examples in the https://github.com/aws/aws-sam-cli-app-templates project: diff --git a/examples/pom.xml b/examples/pom.xml index aca250dae..f290b3951 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,7 +29,9 @@ </description> <modules> - <module>powertools-examples-core</module> + <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> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 10e15d532..9efa7fa63 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -15,7 +15,7 @@ <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> + <sdk.version>2.20.128</sdk.version> </properties> <dependencies> @@ -37,7 +37,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> @@ -107,18 +107,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> 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..b188df501 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 @@ -12,7 +12,7 @@ public class KinesisBatchHandler implements RequestHandler<KinesisEvent, StreamsEventResponse> { - private final static Logger LOGGER = LogManager.getLogger(org.demo.batch.sqs.SqsBatchHandler.class); + private final static Logger LOGGER = LogManager.getLogger(KinesisBatchHandler.class); private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; public KinesisBatchHandler() { diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c9fe249b3..50cfff85d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,9 +14,9 @@ <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.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.119</aws.sdk.version> + <aws.sdk.version>2.20.130</aws.sdk.version> </properties> <dependencyManagement> <dependencies> @@ -132,18 +132,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core/.gitignore b/examples/powertools-examples-core/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index a47d0d26c..f11982477 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -5,17 +5,17 @@ This project demonstrates the Lambda for Powertools Java module - including [tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and [metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). -It is made up of the following: +We provide examples for the following infrastructure-as-code tools: +* [AWS SAM](sam/) +* [AWS CDK](cdk/) -- [App.java](src/main/java/helloworld/App.java) - Code for the application's Lambda function. -- [events](events) - Invocation events that you can use to invoke the function. -- [AppTests.java](src/test/java/helloworld/AppTest.java) - Unit tests for the application code. -- [template.yaml](template.yaml) - A template that defines the application's AWS resources. +For each of the tools, the example application is the same, and consists of the following files: -## Deploy the sample application +- [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. -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 files and deployment process for each tool are described in corresponding README files. ## Test the application @@ -35,13 +35,4 @@ different function calls within 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](src/main/java/helloworld/App.java). - -You can also 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 +[App.java](sam/src/main/java/helloworld/App.java). diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core/cdk/README.md new file mode 100644 index 000000000..f15a24168 --- /dev/null +++ b/examples/powertools-examples-core/cdk/README.md @@ -0,0 +1,36 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with CDK + +This project demonstrates the Lambda for Powertools Java module deployed using [Cloud Development Kit](https://aws.amazon.com/cdk/). + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## 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 + - [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. + +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 + +The minimum to deploy the app should be +```bash +cdk deploy +``` + +If you're running CDK for the first time, you'll need to first run the bootstrap command: +```bash +cdk bootstrap +``` + +## Useful commands + +* `mvn package` compile and run tests +* `cdk synth` emits the synthesized CloudFormation template +* `cdk deploy` deploy this stack to your default AWS account/region +* `cdk diff` compare deployed stack with current state +* `cdk docs` open CDK documentation \ No newline at end of file diff --git a/examples/powertools-examples-core/events/event.json b/examples/powertools-examples-core/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/events/event.json rename to examples/powertools-examples-core/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml new file mode 100644 index 000000000..a1aba1b8d --- /dev/null +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -0,0 +1,200 @@ +<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.3</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="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.1.0</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/src/main/java/helloworld/App.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java similarity index 82% rename from examples/powertools-examples-core/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java index a1ea9a9e3..988da2a73 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java @@ -17,14 +17,11 @@ 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 static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; 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.AWSXRay; -import com.amazonaws.xray.entities.Entity; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -83,38 +80,17 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv log.info(output); }); - threadOption1(); - - threadOption2(); - log.info("After output"); return response .withStatusCode(200) .withBody(output); - } catch (IOException | InterruptedException e) { + } catch (IOException e) { return response .withBody("{}") .withStatusCode(500); } } - private void threadOption1() throws InterruptedException { - final Entity traceEntity = AWSXRay.getTraceEntity(); - assert traceEntity != null; - traceEntity.run(new Thread(this::log)); - } - - private void threadOption2() throws InterruptedException { - Entity traceEntity = AWSXRay.getTraceEntity(); - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> - { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); - anotherThread.start(); - anotherThread.join(); - } - @Tracing private void log() { log.info("inside threaded logging for function"); diff --git a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/src/main/resources/log4j2.xml b/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/cdk/infra/cdk.json b/examples/powertools-examples-core/cdk/infra/cdk.json new file mode 100644 index 000000000..e24ee7b04 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/cdk.json @@ -0,0 +1,36 @@ +{ + "app": "mvn -e -q compile exec:java", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "target", + "pom.xml", + "src/test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true + } +} diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml new file mode 100644 index 000000000..d81118cde --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>software.amazon.lambda.examples</groupId> + <artifactId>cdk</artifactId> + <version>1.18.0-SNAPSHOT</version> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <cdk.version>2.91.0</cdk.version> + <constructs.version>[10.0.0,11.0.0)</constructs.version> + <junit.version>5.10.0</junit.version> + <maven.deploy.skip>true</maven.deploy.skip> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.1.0</version> + <configuration> + <mainClass>cdk.CdkApp</mainClass> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <!-- AWS Cloud Development Kit --> + <dependency> + <groupId>software.amazon.awscdk</groupId> + <artifactId>aws-cdk-lib</artifactId> + <version>${cdk.version}</version> + </dependency> + <dependency> + <groupId>software.constructs</groupId> + <artifactId>constructs</artifactId> + <version>${constructs.version}</version> + </dependency> + + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java new file mode 100644 index 000000000..d564eb9a0 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.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 cdk; + +import software.amazon.awscdk.App; +import software.amazon.awscdk.StackProps; + +public class CdkApp { + public static void main(final String[] args) { + App app = new App(); + + new CdkStack(app, "CdkStack", StackProps.builder() + // If you don't specify 'env', this stack will be environment-agnostic. + // Account/Region-dependent features and context lookups will not work, + // but a single synthesized template can be deployed anywhere. + + // Uncomment the next block to specialize this stack for the AWS Account + // and Region that are implied by the current CLI configuration. + /* + .env(Environment.builder() + .account(System.getenv("CDK_DEFAULT_ACCOUNT")) + .region(System.getenv("CDK_DEFAULT_REGION")) + .build()) + */ + + + // Uncomment the next block if you know exactly what Account and Region you + // want to deploy the stack to. + /* + .env(Environment.builder() + .account("1234567890") + .region("region") + .build()) + */ + + // For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html + .build()); + + app.synth(); + } +} diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java new file mode 100644 index 000000000..8e6b44112 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.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 cdk; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import software.amazon.awscdk.BundlingOptions; +import software.amazon.awscdk.CfnOutput; +import software.amazon.awscdk.Duration; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.StackProps; +import software.amazon.awscdk.services.apigateway.LambdaIntegration; +import software.amazon.awscdk.services.apigateway.RestApi; +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.Tracing; +import software.amazon.awscdk.services.s3.assets.AssetOptions; +import software.constructs.Construct; + +/** + * Defines a stack that consists of a single Java Lambda function and an API Gateway + */ +public class CdkStack extends Stack { + private static final String SHELL_COMMAND = "/bin/sh"; + private static final String MAVEN_PACKAGE = "mvn package"; + private static final String COPY_OUTPUT = "cp /asset-input/target/helloworld-lambda.jar /asset-output/"; + + public CdkStack(final Construct scope, final String id) { + this(scope, id, null); + } + + public CdkStack(final Construct scope, final String id, final StackProps props) { + super(scope, id, props); + + Function helloWorldFunction = createHelloWorldFunction(); + Function helloWorldStreamFunction = createHelloWorldStreamFunction(); + RestApi restApi = createHelloWorldApi(); + + restApi.getRoot().resourceForPath("/hello") + .addMethod("GET", LambdaIntegration.Builder.create(helloWorldFunction) + .build()); + + restApi.getRoot().resourceForPath("/hellostream") + .addMethod("GET", LambdaIntegration.Builder.create(helloWorldStreamFunction) + .build()); + + outputApiUrl(restApi); + } + + private static List<String> createFunctionPackageInstructions() { + // CDK will use this command to package your Java Lambda + return Arrays.asList( + SHELL_COMMAND, + "-c", + MAVEN_PACKAGE + " && " + + COPY_OUTPUT + ); + } + + /** + * Adds API URL to the outputs + * + * @param restApi + */ + private void outputApiUrl(RestApi restApi) { + CfnOutput.Builder.create(this, "HelloWorldApiUrl") + .description("API Gateway endpoint URL for Prod stage for Hello World function") + .value(restApi.getUrl() + "hello").build(); + } + + // Method to create the Lambda function + private Function createHelloWorldFunction() { + List<String> functionPackageInstructions = createFunctionPackageInstructions(); + + Map<String, String> environment = new HashMap<>(); + environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); + environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.1"); + environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); + environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); + + return Function.Builder.create(this, "HelloWorldFunction") + .runtime(Runtime.JAVA_11) + .memorySize(512) + .timeout(Duration.seconds(20)) + .tracing(Tracing.ACTIVE) + .code(Code.fromAsset("../app/", AssetOptions.builder() + .bundling(BundlingOptions.builder() + .image(Runtime.JAVA_11.getBundlingImage()) + .command(functionPackageInstructions) + .build()) + .build())) + .handler("helloworld.App") + .environment(environment) + .build(); + } + + private Function createHelloWorldStreamFunction() { + List<String> functionPackageInstructions = createFunctionPackageInstructions(); + + Map<String, String> environment = new HashMap<>(); + environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); + environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.7"); + environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); + environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); + environment.put("POWERTOOLS_SERVICE_NAME", "hello"); + + return Function.Builder.create(this, "HelloWorldStreamFunction") + .runtime(Runtime.JAVA_11) + .memorySize(512) + .timeout(Duration.seconds(20)) + .tracing(Tracing.ACTIVE) + .code(Code.fromAsset("../app/", AssetOptions.builder() + .bundling(BundlingOptions.builder() + .image(Runtime.JAVA_11.getBundlingImage()) + .command(functionPackageInstructions) + .build()) + .build())) + .handler("helloworld.AppStream") + .environment(environment) + .build(); + } + + // Method to create the REST API + private RestApi createHelloWorldApi() { + return RestApi.Builder.create(this, "HelloWorldApi") + .description("API Gateway endpoint URL for Prod stage for Hello World function") + .build(); + } +} 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 new file mode 100644 index 000000000..29cb15545 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.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 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/README.md b/examples/powertools-examples-core/sam/README.md new file mode 100644 index 000000000..7a4279ae3 --- /dev/null +++ b/examples/powertools-examples-core/sam/README.md @@ -0,0 +1,24 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with SAM + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/). + +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) + +## 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/sam/events/event.json b/examples/powertools-examples-core/sam/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-core/sam/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/sam/pom.xml similarity index 94% rename from examples/powertools-examples-core/pom.xml rename to examples/powertools-examples-core/sam/pom.xml index d402ce09c..bb2e01097 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -35,7 +35,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -106,18 +106,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java new file mode 100644 index 000000000..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e1fd14cea --- /dev/null +++ b/examples/powertools-examples-core/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> \ No newline at end of file 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 new file mode 100644 index 000000000..70dad8d71 --- /dev/null +++ b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.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 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/template.yaml b/examples/powertools-examples-core/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/template.yaml rename to examples/powertools-examples-core/sam/template.yaml diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 38682e164..d0e98ec28 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -48,7 +48,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> @@ -169,18 +169,16 @@ </goals> <configuration> <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.1.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index ac2c7ef1b..72fa621ad 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -22,6 +22,7 @@ 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; @@ -113,7 +114,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv */ private String getPageContents(String address) throws IOException { URL url = new URL(address); - try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { + 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-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 1c7d177f4..19be585a5 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 0ec05cd82..d084df983 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index b3947513f..d4b26d166 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <!-- Test dependencies --> diff --git a/mkdocs.yml b/mkdocs.yml index 62d8d75ce..cde47c815 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,7 +85,7 @@ extra_javascript: extra: powertools: - version: 1.16.1 # to update after each release (we do not want snapshot version here) + version: 1.17.0 # to update after each release (we do not want snapshot version here) repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index 7cdf8a71f..24f71c6bd 100644 --- a/pom.xml +++ b/pom.xml @@ -74,11 +74,11 @@ <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.119</aws.sdk.version> + <aws.sdk.version>2.20.130</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <lambda.core.version>1.2.2</lambda.core.version> + <lambda.core.version>1.2.3</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> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 593b22444..2c878416a 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -9,6 +9,9 @@ <version>2.0.0-SNAPSHOT</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 49b4b5a20..7abaec9d0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -120,19 +120,17 @@ </goals> <configuration> <transformers> - <transformer - implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"> - </transformer> + <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> - </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.1.0</version> + </dependency> </dependencies> </plugin> <plugin> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e8a3273b1..8f3997dbd 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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.89.0</cdk.version> + <cdk.version>2.91.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> @@ -162,7 +162,7 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>2.0</version> + <version>2.1</version> <scope>test</scope> </dependency> <dependency> From 52a1f48ec7baddb6fc2a756b4e0c74d8cb2913ab Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:24:31 +0100 Subject: [PATCH 0448/1008] docs: Change link to absolute versioned path for examples (#1374) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change link to absolute versioned path for examples * Update README.md --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e120bfb00..e351b5c0e 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ## 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/v1.17.0/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). From 7c3e45b81e607c98d64034bc36492795d4eb446e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 13:52:22 +0200 Subject: [PATCH 0449/1008] build(deps): bump aws.sdk.version from 2.20.131 to 2.20.132 (#1390) Bumps `aws.sdk.version` from 2.20.131 to 2.20.132. Updates `software.amazon.awssdk:bom` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:http-client-spi` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:url-connection-client` from 2.20.128 to 2.20.132 Updates `software.amazon.awssdk:sqs` from 2.20.128 to 2.20.132 Updates `software.amazon.awssdk:s3` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:dynamodb` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:lambda` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:kinesis` from 2.20.128 to 2.20.132 Updates `software.amazon.awssdk:cloudwatch` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:xray` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:cloudformation` from 2.20.131 to 2.20.132 Updates `software.amazon.awssdk:sts` from 2.20.131 to 2.20.132 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 4c4a53039..bfa183290 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <maven.deploy.skip>true</maven.deploy.skip> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.131</aws.sdk.version> + <aws.sdk.version>2.20.132</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 8969662a8..60387f2d3 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.131</version> + <version>2.20.132</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 76cdee1f8..dd12c2ae7 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.131</aws.sdk.version> + <aws.sdk.version>2.20.132</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 9f67d215865821a288ee8e4c2a9327c5fa3691f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:50:29 +0200 Subject: [PATCH 0450/1008] build(deps): bump aws.sdk.version from 2.20.132 to 2.20.133 (#1392) Bumps `aws.sdk.version` from 2.20.132 to 2.20.133. Updates `software.amazon.awssdk:bom` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:http-client-spi` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:url-connection-client` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:sqs` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:s3` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:dynamodb` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:lambda` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:kinesis` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:cloudwatch` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:xray` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:cloudformation` from 2.20.132 to 2.20.133 Updates `software.amazon.awssdk:sts` from 2.20.132 to 2.20.133 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index bfa183290..20aa5aeaf 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -16,7 +16,7 @@ <maven.deploy.skip>true</maven.deploy.skip> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.132</aws.sdk.version> + <aws.sdk.version>2.20.133</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 60387f2d3..b030d22fd 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.132</version> + <version>2.20.133</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index dd12c2ae7..8226fa878 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.132</aws.sdk.version> + <aws.sdk.version>2.20.133</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 46124b4fb9d2a53efdff784345ac07d29678a5a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:55:56 +0200 Subject: [PATCH 0451/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1373) --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index d81118cde..5131e22af 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>1.18.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.91.0</cdk.version> + <cdk.version>2.92.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> <maven.deploy.skip>true</maven.deploy.skip> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e13cb65fc..8fd96030b 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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> + <cdk.version>2.92.0</cdk.version> <!-- Don't deploy the e2e tests --> <maven.deploy.skip>true</maven.deploy.skip> From 1fb444abcd6f51142377dc6f8831151a4f288dd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 14:06:23 +0200 Subject: [PATCH 0452/1008] build(deps): bump aws.sdk.version from 2.20.128 to 2.20.133 (#1393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps `aws.sdk.version` from 2.20.128 to 2.20.133. Updates `software.amazon.awssdk:url-connection-client` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:sqs` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:sdk-core` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:kinesis` from 2.20.128 to 2.20.133 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.128 to 2.20.133 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index dd75b597b..9faa4dc24 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -15,7 +15,7 @@ <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.128</sdk.version> + <sdk.version>2.20.133</sdk.version> </properties> <dependencies> From 787aa9d0a811dbf79bef400e718e7171694fb35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 24 Aug 2023 14:19:52 +0200 Subject: [PATCH 0453/1008] chore: secure github actions using hash instead of versions (#1232) * secure github actions using hash instead of versions * delete docs.yaml * fix hash for dcos --------- Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- .github/workflows/auto-merge.yml | 8 +++--- .github/workflows/build-docs.yml | 4 +-- .github/workflows/build.yml | 8 +++--- .github/workflows/dispatch_analytics.yml | 2 +- .github/workflows/docs.yml | 4 +-- .github/workflows/publish.yml | 9 ++++--- .github/workflows/release-drafter.yml | 2 +- .github/workflows/release-prep.yml | 16 ++++++------ .github/workflows/run-e2e-tests.yml | 6 ++--- .github/workflows/secure_workflows.yml | 32 ++++++++++++++++++++++++ .github/workflows/spotbugs.yml | 6 ++--- 11 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/secure_workflows.yml diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 21b5e20e5..5401eedc9 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -17,12 +17,12 @@ jobs: 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 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: ahmadnassri/action-workflow-run-wait@2aa3d9e1a12ecaaa9908e368eaf2123bb084323e # v1.4.4 with: timeout: 300000 - name: 'Download artifact' - uses: actions/github-script@v3.1.0 + uses: actions/github-script@47f7cf65b5ced0830a325f705cad64f2f58dddf7 # v3.1.0 with: script: | var artifacts = await github.actions.listWorkflowRunArtifacts({ @@ -43,7 +43,7 @@ jobs: fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - run: unzip pr.zip - name: Create review - uses: actions/github-script@v3 + uses: actions/github-script@47f7cf65b5ced0830a325f705cad64f2f58dddf7 # v3.1.0 with: script: | var fs = require('fs'); diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index f4a9c4d3f..a4ab6e7de 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -22,9 +22,9 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 with: python-version: "3.8" - name: Capture branch and tag diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ed871a6c..f9a985fb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,9 +54,9 @@ jobs: JAVA: ${{ matrix.java }} AWS_REGION: eu-west-1 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} @@ -64,7 +64,7 @@ jobs: - name: Build with Maven run: mvn -B install --file pom.xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # 3.1.1 + uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.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 @@ -78,7 +78,7 @@ jobs: mkdir -p ./pr echo ${{ github.event.number }} echo ${{ github.event.number }} > ./pr/NR - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 name: Upload artifact with: name: pr diff --git a/.github/workflows/dispatch_analytics.yml b/.github/workflows/dispatch_analytics.yml index 49a276f6f..c93cb5b36 100644 --- a/.github/workflows/dispatch_analytics.yml +++ b/.github/workflows/dispatch_analytics.yml @@ -29,7 +29,7 @@ jobs: environment: analytics steps: - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: aws-region: eu-central-1 role-to-assume: ${{ secrets.AWS_ANALYTICS_ROLE_ARN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9c09e294d..5e37c5f45 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,9 +16,9 @@ jobs: runs-on: ubuntu-latest environment: Docs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 with: python-version: "3.8" - name: Capture branch and tag diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2068c09c5..03f04e0f4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,15 +8,16 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Set up Maven Central Repository - uses: actions/setup-java@v2 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: - distribution: 'zulu' + distribution: 'corretto' java-version: 8 server-id: ossrh server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD + # TODO: use environments https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment 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 @@ -30,7 +31,7 @@ jobs: MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Close issues related to this release - uses: actions/github-script@v5 + uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 with: script: | const post_release = require('.github/workflows/post_release.js') diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 5b27fd671..72bd5c24f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -10,6 +10,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@569eb7ee3a85817ab916c8f8ff03a5bd96c9c83e # v5.23.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml index 345bd2a10..f7a3c74c0 100644 --- a/.github/workflows/release-prep.yml +++ b/.github/workflows/release-prep.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Get current date id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" @@ -18,42 +18,42 @@ jobs: 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 with: find: '## [Unreleased]' replace: | @@ -66,7 +66,7 @@ jobs: regex: false include: CHANGELOG.md - name: Create Release Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@18f7dc018cc2cd597073088f7c7591b9d1c02672 # v3.14.0 with: commit-message: chore:prep release ${{ github.event.inputs.targetRelease }} token: ${{ secrets.RELEASE }} diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 36c9dd97a..c4a8c6fb2 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -41,15 +41,15 @@ jobs: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1.6.1 + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} diff --git a/.github/workflows/secure_workflows.yml b/.github/workflows/secure_workflows.yml new file mode 100644 index 000000000..1430e91d6 --- /dev/null +++ b/.github/workflows/secure_workflows.yml @@ -0,0 +1,32 @@ +name: Lockdown untrusted workflows + +# PROCESS +# +# 1. Scans for any external GitHub Action being used without version pinning (@<commit-sha> vs @v3) +# 2. Scans for insecure practices for inline bash scripts (shellcheck) +# 3. Fail CI and prevent PRs to be merged if any malpractice is found + +# USAGE +# +# Always triggered on new PR, PR changes and PR merge. + + +on: + push: + paths: + - ".github/workflows/**" + pull_request: + paths: + - ".github/workflows/**" + +jobs: + enforce_pinned_workflows: + name: Harden Security + runs-on: ubuntu-latest + permissions: + contents: read # checkout code and subsequently GitHub action workflows + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Ensure 3rd party workflows have SHA pinned + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@555a30da2656b4a7cf47b107800bef097723363e # v2.1.3 diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 0b07bcd81..d314107fa 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -23,11 +23,11 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java JDK 1.8 - uses: actions/setup-java@v2 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: - distribution: 'zulu' + distribution: 'corretto' 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/ From 50e7b56feed62e1c964a0384a0b1968c2affe7b6 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 24 Aug 2023 14:26:16 +0100 Subject: [PATCH 0454/1008] chore: [V2] rename 'core' module to 'common' (#1364) * First rename * POM updates * Rename namespace * Fix * Fix? * More namespace changes * Rename core examples to core utilities * Finish renaming core utilities * Clean up name/description of module * More change * Fix readme --- examples/README.md | 8 ++++---- examples/pom.xml | 6 +++--- .../.gitignore | 0 .../README.md | 0 .../cdk/README.md | 10 +++++----- .../cdk/app/events/event.json | 0 .../cdk/app/pom.xml | 2 +- .../cdk/app/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../cdk/app/src/main/resources/log4j2.xml | 0 .../app/src/test/java/helloworld/AppTest.java | 0 .../cdk/infra/cdk.json | 0 .../cdk/infra/pom.xml | 0 .../cdk/infra/src/main/java/cdk/CdkApp.java | 0 .../cdk/infra/src/main/java/cdk/CdkStack.java | 0 .../infra/src/test/java/cdk/CdkStackTest.java | 0 .../sam/README.md | 0 .../sam/events/event.json | 0 .../sam/pom.xml | 4 ++-- .../sam/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../sam/src/main/resources/log4j2.xml | 0 .../sam/src/test/java/helloworld/AppTest.java | 0 .../sam/template.yaml | 0 pom.xml | 4 ++-- {powertools-core => powertools-common}/pom.xml | 10 ++++------ .../common}/internal/LambdaConstants.java | 2 +- .../internal/LambdaHandlerProcessor.java | 4 ++-- .../common}/internal/SystemWrapper.java | 2 +- .../internal/UserAgentConfigurator.java | 4 ++-- .../main/resources-filtered/version.properties | 0 .../internal/LambdaHandlerProcessorTest.java | 7 +++++-- .../internal/UserAgentConfiguratorTest.java | 13 +++++++------ .../src/test/resources/test.properties | 0 .../src/test/resources/unreadable.properties | 0 powertools-idempotency/pom.xml | 2 +- .../idempotency/internal/IdempotentAspect.java | 2 +- .../persistence/BasePersistenceStore.java | 2 +- .../persistence/DynamoDBPersistenceStore.java | 6 +++--- powertools-large-messages/pom.xml | 2 +- .../largemessages/LargeMessageConfig.java | 2 +- powertools-logging/pom.xml | 2 +- .../logging/internal/LambdaLoggingAspect.java | 16 ++++++++-------- .../internal/LambdaLoggingAspectTest.java | 6 +++--- powertools-metrics/pom.xml | 2 +- .../powertools/metrics/MetricsUtils.java | 2 +- .../metrics/internal/LambdaMetricsAspect.java | 12 ++++++------ .../powertools/metrics/MetricsLoggerTest.java | 18 +++++++++--------- .../internal/LambdaMetricsAspectTest.java | 16 ++++++++-------- powertools-parameters/pom.xml | 2 +- .../parameters/AppConfigProvider.java | 2 +- .../parameters/DynamoDbProvider.java | 2 +- .../powertools/parameters/SSMProvider.java | 2 +- .../powertools/parameters/SecretsProvider.java | 2 +- powertools-tracing/pom.xml | 2 +- .../powertools/tracing/TracingUtils.java | 2 +- .../tracing/internal/LambdaTracingAspect.java | 10 +++++----- .../internal/LambdaTracingAspectTest.java | 2 +- powertools-validation/pom.xml | 2 +- .../validation/internal/ValidationAspect.java | 2 +- 60 files changed, 99 insertions(+), 97 deletions(-) create mode 100644 examples/powertools-examples-core-utilities/.gitignore rename examples/{powertools-examples-core => powertools-examples-core-utilities}/README.md (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/README.md (67%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/app/events/event.json (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/app/pom.xml (99%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/app/src/main/java/helloworld/App.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/app/src/main/java/helloworld/AppStream.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/app/src/main/resources/log4j2.xml (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/app/src/test/java/helloworld/AppTest.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/infra/cdk.json (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/infra/pom.xml (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/infra/src/main/java/cdk/CdkApp.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/infra/src/main/java/cdk/CdkStack.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/cdk/infra/src/test/java/cdk/CdkStackTest.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/README.md (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/events/event.json (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/pom.xml (98%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/src/main/java/helloworld/App.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/src/main/java/helloworld/AppStream.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/src/main/resources/log4j2.xml (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/src/test/java/helloworld/AppTest.java (100%) rename examples/{powertools-examples-core => powertools-examples-core-utilities}/sam/template.yaml (100%) rename {powertools-core => powertools-common}/pom.xml (92%) rename {powertools-core/src/main/java/software/amazon/lambda/powertools/core => powertools-common/src/main/java/software/amazon/lambda/powertools/common}/internal/LambdaConstants.java (96%) rename {powertools-core/src/main/java/software/amazon/lambda/powertools/core => powertools-common/src/main/java/software/amazon/lambda/powertools/common}/internal/LambdaHandlerProcessor.java (96%) rename {powertools-core/src/main/java/software/amazon/lambda/powertools/core => powertools-common/src/main/java/software/amazon/lambda/powertools/common}/internal/SystemWrapper.java (92%) rename {powertools-core/src/main/java/software/amazon/lambda/powertools/core => powertools-common/src/main/java/software/amazon/lambda/powertools/common}/internal/UserAgentConfigurator.java (96%) rename {powertools-core => powertools-common}/src/main/resources-filtered/version.properties (100%) rename {powertools-core/src/test/java/software/amazon/lambda/powertools/core => powertools-common/src/test/java/software/amazon/lambda/powertools/common}/internal/LambdaHandlerProcessorTest.java (96%) rename {powertools-core/src/test/java/software/amazon/lambda/powertools/core => powertools-common/src/test/java/software/amazon/lambda/powertools/common}/internal/UserAgentConfiguratorTest.java (86%) rename {powertools-core => powertools-common}/src/test/resources/test.properties (100%) rename {powertools-core => powertools-common}/src/test/resources/unreadable.properties (100%) diff --git a/examples/README.md b/examples/README.md index 0744c2bb1..79e2133ad 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,9 +5,9 @@ Each example can be copied from its subdirectory and used independently of the r ## 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 + * [SAM](powertools-examples-core-utilities/sam) + * [CDK](powertools-examples-core-utilities/cdk) * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads @@ -54,7 +54,7 @@ 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 diff --git a/examples/pom.xml b/examples/pom.xml index f290b3951..b790cb12e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,9 +29,9 @@ </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-core-utilities/sam</module> + <module>powertools-examples-core-utilities/cdk/app</module> + <module>powertools-examples-core-utilities/cdk/infra</module> <module>powertools-examples-idempotency</module> <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> 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/README.md b/examples/powertools-examples-core-utilities/README.md similarity index 100% rename from examples/powertools-examples-core/README.md rename to examples/powertools-examples-core-utilities/README.md diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core-utilities/cdk/README.md similarity index 67% rename from examples/powertools-examples-core/cdk/README.md rename to examples/powertools-examples-core-utilities/cdk/README.md index f15a24168..acd857ed7 100644 --- a/examples/powertools-examples-core/cdk/README.md +++ b/examples/powertools-examples-core-utilities/cdk/README.md @@ -6,11 +6,11 @@ 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 - - [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. +- [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. 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. 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/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml similarity index 99% rename from examples/powertools-examples-core/cdk/app/pom.xml rename to examples/powertools-examples-core-utilities/cdk/app/pom.xml index a1aba1b8d..6895f4117 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda.examples</groupId> <version>1.16.1</version> - <artifactId>powertools-examples-core-cdk</artifactId> + <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Core</name> 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 100% 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 diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/AppStream.java 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/app/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core-utilities/cdk/app/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core-utilities/cdk/app/src/test/java/helloworld/AppTest.java 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 100% rename from examples/powertools-examples-core/cdk/infra/pom.xml rename to examples/powertools-examples-core-utilities/cdk/infra/pom.xml 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/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core-utilities/cdk/infra/src/test/java/cdk/CdkStackTest.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java rename to examples/powertools-examples-core-utilities/cdk/infra/src/test/java/cdk/CdkStackTest.java 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-core/sam/events/event.json b/examples/powertools-examples-core-utilities/sam/events/event.json similarity index 100% rename from examples/powertools-examples-core/sam/events/event.json rename to examples/powertools-examples-core-utilities/sam/events/event.json diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml similarity index 98% rename from examples/powertools-examples-core/sam/pom.xml rename to examples/powertools-examples-core-utilities/sam/pom.xml index bb2e01097..204a828c0 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,10 +4,10 @@ <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> - <artifactId>powertools-examples-core</artifactId> + <artifactId>powertools-examples-core-utilities</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> + <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics)</name> <properties> <log4j.version>2.20.0</log4j.version> diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/App.java rename to examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/sam/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/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core-utilities/sam/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core-utilities/sam/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/sam/template.yaml b/examples/powertools-examples-core-utilities/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/sam/template.yaml rename to examples/powertools-examples-core-utilities/sam/template.yaml diff --git a/pom.xml b/pom.xml index 24f71c6bd..17fb1a712 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ </licenses> <modules> - <module>powertools-core</module> + <module>powertools-common</module> <module>powertools-serialization</module> <module>powertools-logging</module> <module>powertools-tracing</module> @@ -105,7 +105,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> <version>${project.version}</version> </dependency> <dependency> diff --git a/powertools-core/pom.xml b/powertools-common/pom.xml similarity index 92% rename from powertools-core/pom.xml rename to powertools-common/pom.xml index c244f70fc..721f264e0 100644 --- a/powertools-core/pom.xml +++ b/powertools-common/pom.xml @@ -18,7 +18,7 @@ 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> + <artifactId>powertools-common</artifactId> <packaging>jar</packaging> <parent> @@ -27,10 +27,8 @@ <version>2.0.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> + <name>Powertools for AWS Lambda (Java) library Common Internal Utilities</name> + <description>Internal utilities shared by the Powertools for AWS Lambda (Java) modules. Do not use directly in your project.</description> <url>https://aws.amazon.com/lambda/</url> <issueManagement> <system>GitHub Issues</system> @@ -123,4 +121,4 @@ </plugins> </build> -</project> \ No newline at end of file +</project> 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 96% 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..f43cd52a3 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,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.core.internal; +package software.amazon.lambda.powertools.common.internal; public class LambdaConstants { public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME"; 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 96% 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..1a2b29c2c 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,11 +12,11 @@ * */ -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.getenv; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; 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 92% 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..c537283b5 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() { 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 96% 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..585c38c59 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,9 +12,9 @@ * */ -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; 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-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 96% 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..589aab703 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,14 +12,14 @@ * */ -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 static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -31,6 +31,9 @@ import org.aspectj.lang.Signature; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.internal.SystemWrapper; class LambdaHandlerProcessorTest { diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java similarity index 86% rename from powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java rename to powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java index 2d75bdb3a..9e4d26504 100644 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/UserAgentConfiguratorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java @@ -12,15 +12,16 @@ * */ -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.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 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 static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; + import java.io.File; import java.util.Objects; 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/src/test/resources/unreadable.properties b/powertools-common/src/test/resources/unreadable.properties similarity index 100% rename from powertools-core/src/test/resources/unreadable.properties rename to powertools-common/src/test/resources/unreadable.properties diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 6b9e46a4a..6a39ae6e0 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -57,7 +57,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index 989e88eb7..0b9d729f4 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -14,7 +14,7 @@ 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; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index f58b276fd..8ae3598b9 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -14,7 +14,7 @@ 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; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 82e7b9ead..054f61ef3 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -14,8 +14,8 @@ package software.amazon.lambda.powertools.idempotency.persistence; -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.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; import java.time.Instant; @@ -40,7 +40,7 @@ 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; diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 19793d616..6cffecb73 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -56,7 +56,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> 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-logging/pom.xml b/powertools-logging/pom.xml index e13a88e57..e16dd0a8b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -57,7 +57,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> 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..0a36723f6 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 @@ -17,14 +17,14 @@ 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.common.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.extractContext; +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.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.common.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; 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..942f79d32 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 @@ -24,7 +24,7 @@ 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 software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -59,8 +59,8 @@ 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.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.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; diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 0681ed000..e67aca7e2 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -58,7 +58,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> 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 index 09517d46e..8ab2a2f29 100644 --- 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 @@ -16,7 +16,7 @@ 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.common.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; 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..56a35f67f 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 @@ -16,11 +16,11 @@ 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.common.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.extractContext; +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.serviceName; import static software.amazon.lambda.powertools.metrics.MetricsUtils.hasDefaultDimension; import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; @@ -34,7 +34,7 @@ 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 software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; 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 index 7f234a4d6..89cba6bc4 100644 --- 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 @@ -18,7 +18,7 @@ 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 static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -62,8 +62,8 @@ void tearDown() { @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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.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\""); @@ -92,8 +92,8 @@ void singleMetricsCaptureUtilityWithDefaultDimension() { @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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.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\""); @@ -118,8 +118,8 @@ void singleMetricsCaptureUtility() { @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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.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")) @@ -167,8 +167,8 @@ void shouldThrowExceptionWhenDefaultDimensionIsNull() { 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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.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")) 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..d24cd5bb3 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 @@ -21,7 +21,7 @@ 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 static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -40,7 +40,7 @@ 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.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsColdStartEnabledHandler; @@ -86,8 +86,8 @@ void tearDown() { @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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) @@ -130,8 +130,8 @@ public void metricsWithoutColdStart() { @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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) @@ -174,8 +174,8 @@ public void metricsWithDefaultDimensionSpecified() { @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)) { + MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( + software.amazon.lambda.powertools.common.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 367996e9c..a703bf36b 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -57,7 +57,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> 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 index f2e4faebb..5a1e575dd 100644 --- 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 @@ -25,7 +25,7 @@ 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.common.internal.UserAgentConfigurator; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; 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 index 3a8732e18..363f39d7c 100644 --- 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 @@ -28,7 +28,7 @@ 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.common.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; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index 549cdfbab..10bb70c15 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -27,7 +27,7 @@ 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.common.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; 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 index 2612f6c7f..b77f501f2 100644 --- 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 @@ -26,7 +26,7 @@ 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.common.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; diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 5f397aa9f..0c9ed8f71 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -58,7 +58,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> 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..98aaf9c57 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,7 +14,7 @@ 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; 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..198cb7f34 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; 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..a676bf683 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 @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.tracing.handlers.PowerToolDisabled; import software.amazon.lambda.powertools.tracing.handlers.PowerToolDisabledForStream; import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabled; diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 1b939f46f..5868629de 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -58,7 +58,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-core</artifactId> + <artifactId>powertools-common</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> 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..0d71104f3 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; From 8d032490a3da5cd891fc80b163a5f9f010c10734 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:09:18 +0100 Subject: [PATCH 0455/1008] docs: Update gradle configuration readme (#1359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update gradle configuration readme * Move into configuration * Starting gradle example * Remove * Start again * Update documentation for gradle * Clarify gradle versions a bit better * Starting gradle example * Cleanup gradle example * Build gradle example * Fix build hopefully * Add gradle wrapper * Formatting * Update docs/index.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Updated README with sam instructions --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- .github/workflows/build.yml | 5 + .gitignore | 2 + README.md | 16 +- docs/index.md | 17 +- examples/powertools-examples-core/README.md | 5 + .../powertools-examples-core/gradle/README.md | 38 +++ .../gradle/build.gradle | 35 +++ .../gradle/events/event.json | 62 +++++ .../gradle/gradle/wrapper/.gitignore | 2 + .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../powertools-examples-core/gradle/gradlew | 248 ++++++++++++++++++ .../gradle/gradlew.bat | 92 +++++++ .../gradle/src/main/java/helloworld/App.java | 107 ++++++++ .../src/main/java/helloworld/AppStream.java | 38 +++ .../src/test/java/helloworld/AppTest.java | 24 ++ .../gradle/template.yaml | 71 +++++ 17 files changed, 767 insertions(+), 2 deletions(-) create mode 100644 examples/powertools-examples-core/gradle/README.md create mode 100644 examples/powertools-examples-core/gradle/build.gradle create mode 100644 examples/powertools-examples-core/gradle/events/event.json create mode 100644 examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore create mode 100644 examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/powertools-examples-core/gradle/gradlew create mode 100644 examples/powertools-examples-core/gradle/gradlew.bat create mode 100644 examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java create mode 100644 examples/powertools-examples-core/gradle/template.yaml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9a985fb9..73ae67553 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,6 +63,11 @@ jobs: cache: 'maven' - name: Build with Maven run: mvn -B install --file pom.xml + - name: Build Gradle Example + if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 + run: | + cd examples/powertools-examples-core/gradle + ./gradlew build - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once diff --git a/.gitignore b/.gitignore index 04e85211d..b404d2cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ example/HelloWorldFunction/.gradle example/HelloWorldFunction/build /example/.gradle/ /example/.java-version +.gradle +build/ \ No newline at end of file diff --git a/README.md b/README.md index e351b5c0e..2afb40e5d 100644 --- a/README.md +++ b/README.md @@ -130,11 +130,18 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam <summary><b>Gradle - Java 11+</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 +150,7 @@ 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 "org.aspectj:aspectjrt:1.9.8.RC3" } sourceCompatibility = 11 @@ -159,6 +167,12 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' } + // the freefair aspect plugins targets gradle 7.6.1 + // https://docs.freefair.io/gradle-plugins/6.6.3/reference/ + wrapper { + gradleVersion = "7.6.1" + } + repositories { mavenCentral() } diff --git a/docs/index.md b/docs/index.md index d3e487174..6af4c5e8d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -211,10 +211,17 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "Gradle Java 11+" ```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() @@ -233,10 +240,18 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "Gradle Java 1.8" ```groovy + plugins { id 'java' id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' } + + // the freefair aspect plugins targets gradle 7.6.1 + // https://docs.freefair.io/gradle-plugins/6.6.3/reference/ + wrapper { + gradleVersion = "7.6.1" + } + repositories { mavenCentral() diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index f11982477..d690b01c5 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -6,9 +6,14 @@ This project demonstrates the Lambda for Powertools Java module - including [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/) +We also provide an example showing the integration of SAM, Powertools, and Gradle: + +* [AWS SAM with a Gradle build](gradle/) + 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. diff --git a/examples/powertools-examples-core/gradle/README.md b/examples/powertools-examples-core/gradle/README.md new file mode 100644 index 000000000..c7c798d5d --- /dev/null +++ b/examples/powertools-examples-core/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 1.8 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/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle new file mode 100644 index 000000000..e7367c246 --- /dev/null +++ b/examples/powertools-examples-core/gradle/build.gradle @@ -0,0 +1,35 @@ + +plugins { + id 'java' + id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" +} + +wrapper { + gradleVersion = "7.6.1" +} + +compileJava { + sourceCompatibility = "1.8" + targetCompatibility = "1.8" + + ajc { + enabled = true + } +} + +repositories { + 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.11.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' + aspect 'software.amazon.lambda:powertools-tracing:1.17.0' + aspect 'software.amazon.lambda:powertools-logging:1.17.0' + aspect 'software.amazon.lambda:powertools-metrics:1.17.0' + testImplementation 'junit:junit:4.13.2' +} + diff --git a/examples/powertools-examples-core/gradle/events/event.json b/examples/powertools-examples-core/gradle/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/examples/powertools-examples-core/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/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore new file mode 100644 index 000000000..59c09e205 --- /dev/null +++ b/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore @@ -0,0 +1,2 @@ +!gradle-wrapper.jar + diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340 GIT binary patch literal 63375 zcmb5VV{~QRw)Y#`wrv{~+qP{x72B%VwzFc}c2cp;N~)5ZbDrJayPv(!dGEd-##*zr z)#n-$y^sH|_dchh3@8{H5D*j;5D<{i*8l<n`R`94An31eIWbisdMSBvMo=KdzZu#! z2=IUVG7$V4U%UUmhH^skQsQDNstj`C4{}qJvNH4x^YAkCG&57PP0CD5tUr(Lr|8F| zrsbw-rRacR&cjU84vV#^0hr{ahs87@nB*8}#Ta+ach127GUL}I|L4%azP25lE&lDO z{@DihA2t@wMy9rA|5sDgzngkE8#y|fIse-(VW+DelrTU*`j|jKH2?E168}A!#$SIR zXJlp1U}9_J;*z5Y>5IFJ|DjL!e)upfGNX(kojugZ3I`oH1PvW`wFW_ske0j@lB9bX zO;2)`y+|!@X(fZ1<2n!Qx*)_^Ai@Cv-dF&(vnudG?0CsddG_&Wtae(n|K59ew)6St z#dj7_(Cfwzh$H$5M!$UDd8=4>IQsD3xV=lXUq($;(h*$0^yd+b{qq63f0r_de#!o_ zXDngc>zy`uor)4A^2M#U*DC~i+dc<)Tb1Tv&~Ev@oM)5iJ4Sn#8iRw16XXuV50BS7 zdBL5Mefch(&^{luE{*<o)$0CtHMXCiFaqU;N{t<$9@JbXquVr@cf{y~BNB(J5=Tji zlK?_g|E;1zl$VJ=#ZmElT~Y6jy-|?2PUv}kl<0irKUHY7@2t={_gVdY)lv8kM+ad9 zC<O%>5qtCZk$oFr3<io|2$Itc(&(T+V0vhN)K$Fl^c3u8y`}{@R7L#c1&Qu_+u$L| zkw6sZeUEd0xxV1r@X7Bj^XUCX<ecNL?GSk}zL!>RH=H!c3wGR=HJ(yKc_re_X9pD` zJ;uxPzUfVpgU>DSq?J;I@a+10l0ONXPcDkiYcihREt5~T<to{?YLB3#Ek~Bd_FRTK z3SVU)NWfW~bevBhSgga`J`3XaEJ;UR&tR-QNI#e+fX1mkLg(kYRIlBUeP!g)rVvkV zmBQF>5Gb}sT0+6Q;AWHl`<y=xe2MOa)>S5dV>lv%-p9l#xNNy7ZCr%cyqHY%TZ8Q4 zbp&#ov1*$#grNG#1vgfFOLJCaNG@K|2!W&HSh@3@Y%T?<RDDZ2kvE4KZX_tTk{8@Y z+1Qu}v&0qF!3ps~B5R6-#N&o4vQEcX3!~lWKK-JjRoUbPQR)>3YI75bJp!VP*$*!< z;(ffNS_;@RJ`=c7yX04!u3JP*<8jeqLHVJu#WV&v6wA!OYJS4h<_}^QI&97-;=ojW zQ-1t)7wnxG*5I%U4)9$wlv5Fr;cIizft@&N+3<m!sp`}{5>2O%B{R1POm$oap@&f| zh+5J{>U6ftv|vAeKGc|zC=kO(+l7_cLpV}-D#oUltScw})N>~JOZLU_0{Ka2e1evz z{^a*ZrLr+JUj;)K&u2CoCAXLC2=fVScI(m_p~0FmF>>&3DHziouln?;sxW`NB}cSX z8?I<poVWwH93~xX>sJB)Z=aYRz!X=yJn$kyOWK%rCYf-YarNqKzmWu$ZvkP12b4qH z<cj_@_^h^p^q&$rHm}tFrF$o@p+N@Luju~MbeZxq_WbMvMAonH{#8FcaQx#1Ex963 zthr*D;hp#t`U%;8Lw{en#r&PBH>hS9Q>j<}(*frr?z<%9hl*i^#@*O2q<G8@m-E{I z`}pP(W$_?tQz?qiq)AkeSb{O1HEI<O&IPY2fz^)h2U5WFf)$o|GVN9!>(Z^CN)c2c z>1B~D;@YpG?G!Yk+*yn4vM4sO-_!&m6+`k|3zd;8DJnxsBYtI;W3We+FN@|tQ5EW= z!VU>jtim0Mw#iaT8t_<+qKIEB-WwE04lBd%Letbml9N!?SLrEG$nmn7&W(W`VB@5S zaY=s<l}}fvx=2PUlRXVFqYw_pix_=MLAKV-vfffnNa-G}V}-DjqeGu81{_6c7DT4* zgNTK&HNdPkT}|m;Wopt-pwH(=vK!Mcs#L3p7EuhKtdS*$(gi7K6)2mt;vO}}@U2?@ zic8*RBj6lGpirRD%IH>Ew2}i@F_1P4OtEw?xj4@D6>_e=m=797#hg}f*l^`AB|Y0# z9=)o|%TZFCY$SzgSjS|8AI<m~)~<LWT=KD$snpvb;<|raYO=8NN=pEex{aVNGen|i z4hGyCiz+M`>-%J4x}J)!IMxY3_KYze`_I=c1nmrk@E8c9?MVRu)7+Ue79|<R7R(*W zmGI9WxS<;F_rj?)6ZJ2+&*@e<mlh^Wi>)rBX7tVB7U|w4*h(;Gi3D9le49B38`wuv zp7{4X^p<CFK*NrFla6?I(q;<C*K@ag4>+K4*$@gU(Tq3K1a#3SmYhvI42)GzG4f|u zwQFT1<JTz}_6=eHFU^e2CZtm7+S~2?G10jrHLa$Yc>n_=n|jpi=70-yE9LA+d*T8u z`=VmmXJ_f6WmZve<c3j)L*cT@L>ZPct$Cgu^~gFiyL>Lnpj*6ee>*0pz=t$IJ}+rE zsf@>jlcG%Wx;Cp5x)YSVvB<GcbWPQ65t~gc{a(L|Y**_KX&N^LV{4p;>1$yyY1l&o zvwX=D7k)Dn;ciX?Z)Pn8$flC8#m`nB&(8?RSdBvr?>T9?E$U3u<MGKL6<gI3+cigX zr2;7xjAPPdw|q3|5<Av+0yh@5pePF?so63EF4(f;!m<(9QF+GK>IX7T?$v4dWCa46 z+&`ot8ZTEgp7G+c52oHJ8nw5}a^dwb_l%MOh(ebVj9>_koQP^$2B~eUfSbw9RY$_< z&DDWf2LW;b0ZDOaZ&2^i^g+5uTd;GwO(-bbo|P^;CNL-<vp1D1$R<L}_zoyFQ(?^n zl`6VAFTjED$Nit=axARyg>%?9mRmxEw~5&z=X^Rvbo^WJW=n_%*7974RY}JhFv46> zd}`2|qkd;89l}R;i~9T)V-Q%K)O=yfVKNM4Gbacc7AOd>#^&W&)Xx!Uy5!BHnp9kh z`a(7MO6+Ren#>R^D0K)1sE{Bv>}s6Rb9MT14u!(NpZOe-?4V=>qZ>}uS)!y~;jEUK z&!U7Fj&{WdgU#L0%bM}SYXRtM5z!6M+kgaMKt%3FkjWYh=#QUpt$XX1!*XkpSq-pl zhMe{muh#<vd{NzT8hJO~2nwSu@|uKui`Q8EdXeGz4>knk{9_V3%qdDcWDv}v)m4t9 z<k^O7as2~K;#kz6&_j;+XcIB_r9LslJ=plZ802GD7!wKurp5N7C0N7MrBiyAL~c=u zE%@soR=E%Ksd7<Rzkb}c1=?E^tRZO%BD}eh;$H);oB)^Nt6e4N2J+}eE=O>Qhv{;} zc{}#V^N3H>9mFM8`i`0p+fN@GqX+kl|M94$BK3J-X`Hyj8r!#x6Vt(PXjn?N)qedP z=o1T^#<s;C9Ui_c^t!}2S-XqPF?-?4;fe4415B~F0>?1^a{;bZ&x`U{f?}TMo8ToN zkHj5<VbXBbPLm`saJ%OL;G18~%@f$_blKkP1#<P0FY;5DtZHS)$u-A?Yn3SA3J@bT zA1d!HbKV+f1Ugw07K&jwzua_~#;P<Rn>v|}r}wDEi7I@)Gj+S1aE<Lr;qg@51w32$ zyxn{bK>-GdnLN+$hw!=DzglMaj#{qjXi_dwpr|HL(gcCXwGLEmi|{4&4#OZ4ChceA zKVd4K!D>_N=_X;{poT~4Q+!Le+ZV>=H7v1*l%w`|`Dx8{)McN@NDlQyln&N3@bFpV z_1w~O4EH3fF@IzJ9kDk@7@QctFq8FbkbaH7K$iX=bV~o<VCiV&YRTZ}?C^!Fu2yC) zv{Vzb(sB&ct#XXgvg1<Aax>#gfh?2JD6lZf(XP>~DACF)fGFt)X%-h1yY~MJU{nA5 ze2zxWMs{YdX3q5XU*9hOH0!_S24DOBA5usB+Ws$6{|AMe*joJ?RxfV}*7AKN9V*~J zK+OMcE@bTD>TG1<D&k;gXJl_GYh`aH;$ZLob;4%Of6;ZSs-6Ri5E?%yZ1lwjNo$M0 zh+s;*GL1qh63T)l8*vTt!qBLZ)~cQ14>*yc?*qGqjBN8mgg@h1cJLDv)0!WRPIkC` zZrWXrceVw;fB%3`6kq=a!pq|hFIsQ%ZS<kf2ia2#pBvu`A3V%+`AJvHB*NUK3~nQF zw*gxnx7LCX(Z^1w*|SqdvT{$S%V#1K_mVQ7La-Aw%y<w}ejK@Lu|-CGm40~>lo~)D z|64!aCnw-?>}AG|*iOl44KVf8@|joXi&|)1rB;EQWgm+iHfVbgllP$f!$<xMKNPGw z75lQ-&s?W5309;y6gIrMn!YgKCh2h_t)HK6EcT@xYc0sgM!#>Wf42%NO5b(j9Bw6L z;0dpUUK$5GX4QbMlTmLM_jJt!u<VK-KUt7Z%d43gTkafnEz;tKrLF`kq7eb@)^GVH zVzlnCl^>r`_0~$b#BB7FL*%XFf<<YlClUogc56^3Yyh4jgqXW7(#Qu|X^(|f$!!nL zr<Jlyt{`j<%HJ7(Ibr+qi51D$ikY1it_}mi&OTSv%-y{FbY?e9I<zP))1O}CdnlMB z)E{0F(+ck9%;u_OGgFgau=Rw8qE6u}01y?;f@M5NLv*P|4@P3@#u%P9aWCL)&PJT| zX@dygu5XWA26#e~n6RWn&*Bl^^VBtoVJBn^bDnW4mHo4ME6_YI9>b__1o)Ao<oAII zl<ghkn)lbTvrX_mEpa~6_wy3!knhoEQy$s)O&Eje&DuVJ{~mIy!7WXiU&-a=SC+^7 zzq_L1{|UJN-6?C-bu@6*&_3i@#`~C#P@p9X(Ce2%iic!mTBMYuD`LZ<OM}*McxA(w zkj(d|!1fegueE#LwG9egYdYR8KktNowE4+1AfZ@IuxN3gT>3rlobbN8-(T!1d<VYe z=uu*dc`@_NH-vid1r!+qd!W<p6Hp2sR=vY4yh`?ujy)PePx7Y^!w{->-bR8D3S0@d zLI!*GMb5s~Q<&sjd}lBb8Nr0>PqE6_!3!2d(KAWFxa{hm`@u|a(%#i(#f8{BP2wbs zt+N_slWF4IF_O|{w`c~)Xvh&R{Au~CFmW#0+}MBd2~X}t9lz6*E7uAD`@EBDe$>7W zzPUkJx<`f$0VA$=>R57^(K^h86>09?>_@M(R4q($!Ck6GG@pnu-x*exAx1jOv|>KH zjNfG5pwm`E-=ydcb+3BJwuU;V&OS=6yM^4Jq{%AVqnTTLwV`AorIDD}T&<zk=U4_F z%akElkXp@CbeS<cl%y^#t}u_*o+Kw^Xa%!S>jWr8pB&j28fVtk_y*JRP^t@l*($UZ z6(B^-PBNZ+z!p?+e8@$&jCv^EWLb$WO=}Scr$6SM*&~B95El~;W_0(Bvoha|uQ1<y zI;g~pq<puh8JAZSg`e`{9Ul}WlQxSt?3%o&hA!;)cXW-;B<UPjMu}?EtHvVS7g>T< zO$%_oLAwf1bW*rKWmlD+@CP&$ObiDy=nh1b2ejz%LO9937N{LDe7gle4i!{}I$;&Y zkexJ9Ybr+lrCmKWg&}p=`2&Gf10orS?<wSRKh%(i*-EzBy^*(nk#EV0x%s+gVr5#i zF*^yn?NFz@z)jkaF%P~*zrnDtj18`Mit$=8TVU0_Xu0XQT-29W)`{}4Y{_WLO}la2 z3kum*Acd(?w(30MQ0iXECV4}56Baro5eg?Ji{&xv>4$Vr<ApIaAwLyRgnDz_63EnQ zb0F~DwJxa8Y6V&P@8Y;IWU23PX|5YXwRO5>zWidT=*6{KzOGMo?KI0>GL0{iFWc;C z+LPq%VH5g}6V@-tg2m{C!-$fapJ9y}c$U}aUmS{9#0CM*8pC|sfer!)nG7Ji>mfRh z+~6CxNb<thuojmgyDIx-O?L~|1OMp?{&5*5nw(NYRF76i1VE!yuFbdk^SXpYh9d!e zisi>>6eWKMHBz-w2{mLL<sWnSR{lp+GVAVGNcs2U?&%}ZbUT({ThKL33h5&godIvq z#4SFCl~dpzw{Kf9GWC*<(5@{J-YWs96Ulo#)6da2L@e?NLIhPLoWud(Gbix6rPhyM z+#ezG31H`whsp_@rDLe9hoK&0hz}tS!3q2%y1yY-p%>wdA7dA-qfTu^A2yG1+9s5k zcF=le_UPYG&q!t5Zd_*E_P3Cf5<i9lV%B>T6821bO<oZ<I;eq^g7*0L=5+o%xOyh3 zV}b+qIu^3vM+=S`g6~mUfaz2O^0b~+Y02%irk{L(|9!#otC{hV00sh*`O?q-K|B9x zc@lEAaI-VBcNOzAF>`daa`;DODm8Ih8k89=RN;-asHIigj`n=ux>*f!OC5#;X5i;Q z+V!GUy0|&Y_*8k_QRUA8$lHP;GJ3UUD08P|ALknng|YY13)}!!HW@0z$q+kCH%xet zlWf@BXQ=b=4}QO5eNnN~CzWBbHGUivG=`&eWK}<gH9L&>beuV*;?zt=P#pM*eTuy3 zP}c#}AXJ0OIaqXji78l;YrP4sQe#^pOqwZUiiN6^0RCd#D271XCbEKpk`HI0IsN^s zES7YtU#7=8gTn#lkrc~6)R9u&SX6*Jk4GFX7){E)WE?pT8a-%6P+zS6o&A#ml{$WX zABFz#i7`DDlo{34)oo?bOa4Z_lNH>n;f0nbt$JfAl~;4QY@}NH!X|A$KgMmEsd^&Y zt;pi=>AID7ROQfr;MsMtClr5b0)xo|fwhc=qk33wQ|}$@?{}qXcmECh>#kUQ-If0$ zseb{Wf4VFGLNc*Rax#P8ko*=`MwaR-DQ8L8V8r=2N{Gaips2_^cS|oC$+yScRo*uF zUO|5=?Q?{p$inDpx*t#Xyo6=s?bbN}y>NNVxj9NZCdtwRI70jxvm3!5R7yiWjREEd zDUjrsZhS|P&|Ng5r+f^kA6BNN#|Se}_GF>P6sy^e8kBrgMv3#vk%m}9PCwUWJg-AD zFnZ=}lbi*mN<K#(vlYbGZAX^KQmjvAYCRG*UOU`z2$j+74AdgXr3(r`Z*t~vhyGOF z)w@e8rCo#wjxU`Xq#TN0kURQy8Y45b@jCRNbbQi7ac)K;Y9F%JPMNFNffNKTTeU*T zHQTmYG^Gu1I@&Jv`71fu(BSKE_ZcDAC6eM{-i#Ce{raky!z_b9d|h7zARvnW>-AOm zCs)r=*YQAA!`e<R&0)*Xk7%|k&^;uv62@(5&ac_hW*F9=TfvBeS~Qh~EX`oba74cG z_zl_hTH19>#1N>aHF=bb*z*hXH#Wl$z^o}x##ZrUc=kh%OHWhp=7;?8%Xj||@V?1c ziWoaC$^&04;A|T)!Zd9sU<cT<Lad$0pGXX1w=fLRLa7aSLO9sinK2%NmW<mIFjiuc z-cT9?*>zE&$ODyJ<B|PnBKliB6c94vLSghm91pGb$1o^7rM2a&%c}D$u}j(J@zRz# zi%s0i4BD9?+o@$HB_##NjTPLR3oh&PgIxvX>aBpvqsw19Uiuq{i#VK1!htkdRWBnb z`{rat=nHArT%^R>u#CjjCkw-7%g53|&7z-;X<Ac^=g(0g1=gRkv{@6{)+2MuRw4?q zSyffm46G$5&03=o2M%0CNA&bH8`|Q+lj*sOSA!_VPI<qibefjTL~ySR5|HpXSu-Wk zjm)E}CNtR?XF>+ewb?OLWiV|#nuc8mp*LuGSi3IP<<*Wyo9GKV7l0Noa4Jr0g3p_$ z*R9{qn=?IXC#WU>48-k5V2Oc_>P;4_)J@bo1|pf=%Rcbgk=5m)CJZ`caHBTm3%!Z9 z_?7LHr_BXbKKr=JD!%?KhwdYSdu8XxPoA{n8^%_lh5cjRHuCY9Zlpz8g+$f@bw@0V z+6DRMT9c|>1^3D|$Vzc(C?M~iZurGH2pXPT%F!JSaAMdO%!5o0uc&iqHx?ImcX6fI zCApkzc~OOnfzAd_+-DcMp&AOQxE_EsMqKM{%dRMI5`5CT&%mQO?-@F6tE*xL?aEGZ z8^wH@wRl`Izx4sDmU>}Ym{ybUm@F83qqZPD<I_<D@SDBXpcm$%pP;@}1x+1rECR~6 z%mPO96ZtCMfz6TZL_tB_o<jX(0%{4O*=Jpf{(}rOT%n6FF#H{^%{gCRk)ccFmy zlAyZVmLT4N#~F)~@`1bcBU<gu4>6nFm?t?(7>h*?`fw)L3t*l%*iw0Qu#?$5eq!Qc zpQvqgSxrd83NsdO@lL6#{%lsYXWen~d3p4fGBb7&5xqNYJ)yn84!e1PmPo7ChVd%4 zHUsV0<QfI}<M8O`g)!{5VcjkDZIjCu8(aqo6;;=sPlL7o>Mh?VpzZD=A6%)Qrd~i7 z96*RPbid;BN{Wh?adeD_p8YU``kOrGkNox3D9~!K?w>#kFz!4lzOWR}puS(DmfjJD z`x0z|qB33*^0mZdM&6$|+T>fq>M%yoy(BEjuh9L0>{P&XJ3enGpoQRx`v6$txXt#c z0#N?b5%srj(4xmPvJxrlF3H%OM<X=kF451d5XRpaI3Rddya;o<MiVe63o}q9!6}_c zo)Za~rjO%XWDn6$-;t})ZmU#rhSPD)qiCJFwO-$XixQk0X*gbZ^iyuL^ft*8RskMZ z61oYTT##Iok;Rg+0anh212gV|jFfog*GZX}VV7x@cwuYn2k0l|CdXJ3M&=>B!jvfy z;wx8RzU~lb?h_}@V=bh6p8PSb-dG|-T#A?`c&H2`_!u+uenIZe`6f~A7r)`9m8atC zt(b|6Eg#!Q*DfRU=Ix`#B_dK)nnJ_+>Q<1d7W)eynaVn`FNuN~%B;uO2}vXr5^zi2 z!ifIF5@Zlo0^h~8+ixFBGqtweFc`C~JkSq}&*a3<b*AGX+4JAVcr=k1@(BfrL*bH3 zB2tsVQA!i($9n4x3TKj4fyB9v6dVeLF9ce$&KiuST#O+L;`7)j^T{2s!k-fHs3AFL z;*i&)+V}HhjAA_Rcq9bBAlY`@fUE4EXY~}ibwoho??7zC!;EPmIuC?iA|=eX-ry23 zydv?^AaCLg6^~XLVJgXk5t3-5-l5#+-WH4#R6H+-pH>C}L?b5Mh-bW=e)({F_g4O3 zb@SFTK3VD9QuFgFnK4Ve_pXc3{S$=+Z;;4+;*{<o#P)-O8F)a#4K`1Xm|~?q)i|U3 zYQ`j;(xom@I4xe9dA2S6y-d+xYe;^;M{B3B`KM&`C&=Gb<o8unUCEbv9DNO{|Er29 z8aca|Ig>H}Rc;845rP?DLK6G5Y-xdUKkA6E3Dz&5f{F^FjJQ(NSpZ8q-_!L3LL@H* zxbDF{gd^U3uD;)a)sJwAVi}7@%pRM&?5IaUH%+m{E)DlA_$IA1=&jr{KrhD5q&lTC zAa3c)A(K!{#nOvenH6XrR-y>*4M#DpTTOGQEO5Jr6kni9pDW`rvY*fs|ItV;CVITh z=`rxcH2nEJpkQ^(;1c^hfb8vGN;{{oR=qNyKtR1;J>CByul*+=`NydWnSWJR#I2lN zTvgnR|MBx*XFsfdA&;tr^dYaqRZp*2NwkAZE6kV@1f{76e56eUmGrZ>MDId)oqSWw z7d&r3qfazg+W2?bT}F)4jD6sWaw`_fXZGY&wnGm$FRPFL$HzVTH^MYBHWGCOk-89y zA+n+Q6EVSSCpgC~%uHfvyg@ufE^#u?JH?<73A}jj5iILz4Qqk5$+^U(SX(-qv5agK znUkfpke(KDn~dU0>gdKqjTkVk`0`9^0n_wzXO7R!0Thd<OO)*@xLj!dA|^KI{(+g5 z4&&;v3+^PaBya7Rnu#!)XYc}vIWqv)^MY!O)bd!?B<}^dB*bn^DfNh`{LBe@BaZ7K z79Vu@{$pu8y#gTfUJ?t()owinp0&lUvSWm~f6lhfPNSF&`a(>@S;U`y)VVP&mOd-2 z(hT(|$=>4FY;CBY9#_lB$;|Wd$aOMT5<N7HW=#J5xiuClp{tnl<jC$q#gWfwjqeAY zV;sA^S=5DG9oD|_sR@+2OPrAQibqT{OGVV96@Akgvd57K5T@^KQN}?9VsiR^`m+&4 z6Wo=&#vs$B<Y9Yj#aZVD^shN}siQ$PUDTmt>O_3}DYXEHn&Jrc3`2JiB`b6X@EUOD zVl0S{ijm65@n^19T3l%>*;F(?3r3s?zY{thc4%AD30CeL_4{8x6&cN}zN3fE+x<9; zt2j1RRVy5j22-8U8a6$pyT+<`f+x2l$fd_{qEp_bfxfzu>ORJsXaJn4>U6oNJ#|~p z`*ZC&NPXl&=vq2{Ne79AkQncuxvbOG+28*2wU$R=GOmns3W@HE%^r)Fu%Utj=r9t` zd;SVOnA(=MXgnOzI2@3SGKHz8HN~Vpx&!Ea+Df~`*n@8O=0!b4m?7cE^K*~@fqv9q zF*uk#1@6Re_<^9eElgJD!nTA@K9C732tV~;B`hzZ321Ph=^BH?zXddiu{Du5*IPg} zqDM=QxjT!Rp|#Bkp$(mL)aar)f(dOAXUiw81pX0DC|Y4;>Vz>>DMshoips^8Frdv} zlTD=cKa48M>dR<>(YlLPOW%rokJZNF2gp8fwc8b2sN+i6&-pHr?$rj|uFgktK@jg~ zIFS(%=r|QJ=$kvm_~@n=ai1lA{7Z}i+zj&yzY+!t$iGUy|9jH#&oTNJ;JW-3n>DF+ z3aCOzqn|$X-Olu_<wOD+V1cxb0Z}9)qPN6k=yG%7N(OXSN(!|;<~~&ZV7<|dWJ*$O zcc8BYF-@yY+0BQ2=@gx;O-;QS>p7brzn`uk1F*N4@=b=m;S_C?#hy{&NE#3Hk<sC+ z@RVY+px5c26lyz%OfzZTn@(3s>ATrg?enaVGT^$qIjvgc61y!T$9<1B@?_ibtDZ{G zeXInVr5?OD_nS_O|CK3|RzzMmu+8!#Zb8Ik;rkIAR%6?$pN@d<0dKD2c@k2quB%s( zQL^<_EM6ow8F6^wJN1QcPOm|ehA+dP(!>IX=Euz5qqIq}Y3;ibQtJnkDmZ8c8=Cf3 zu`mJ!Q6wI7EblC5RvP*@)j?}W=WxwCvF3*5Up_`3*a~z$`wHwCy)2risye=1mSp%p zu+tD6NAK3o@)4VBsM!@);qgsjgB$kkCZhaimHg&+k69~drbvRTacWKH;YCK(!rC?8 zP#cK5JPHSw;V;{Yji=55X~S+)%(8fuz}O>*F3)hR;STU`z6T1aM#Wd+FP(M5*@T1P z^06O;I20S<pPBYLx^KQ-E#4lJKf0#2<$Urm^J75xe^_~ooFOaniz#EWEnAqL5nl;d z;Y?#EUwvbZHb_{bP#Z+Xi6;``%`1xT4(Qh>k!bxW<-O;E081KRdHZrtsGJflFRRFS zdi5w<L%xAIZMaxEN{|sC`S2LX=HNoo7yNMxu?JQZn!#EHpMVSC`Z-rSU>9OVDGSL3 zNrC7GVsGN=b;YH9jp8Z2$^!K@h=r-xV(aEH@#JicPy;A0k1>g1g^XeR`YV2HfmqXY zYbRwaxHvf}OlCAwHoVI&QBLr5R|THf?nAevV-=~V8;gCsX>jndvNOcFA+DI+zbh~# zZ7<oMFIjT?dRB+;KT%*|Gjj)Lv;R$(lsDCpKH})P;^<HgAW$|Ic$UC!!9k_^)<VFb z+R-4(+=Oiwvgpt>`qNk&w+_+Yp!}j;OYxIfx_{f0-ONc?mHCiCUak=>j>~>YR4#w# zuKz~UhT!L~GfW^CPqG8Lg)&Rc6y^{%3H7iLa%^l}cw_8UuG;8nn9)kbPGXS}p3!L_ zd#9~5CrH8xtUd?{d2y^PJg+z(xIfRU;`}^=OlehGN2=?}9yH$4Rag}*+AWotyxfCJ zHx=r7ZH>j<rs-kbQ;s$ZI)B{YCAt<1f8=Z!C#+cW@(f}Vui2`~bhsJNt4X5FEVH#V zmS~5qafT)ZOfofB3RY^p$qiO+hKg5MB@4BiWOlTuD_ywdEG^^`73sk%6$@P{w!m`d zG%&#}O$F6xyMIL5Ey>2kV?%7WTtp+-HMa0)_*DBBmC{sd$)np&GEJ__kEd`xB5a2A z*J+yx>4o#ZxwA{;NjhU*1KT~=ZK~GAA;KZHDyBNTaWQ1+;tOFFthnD)DrCn`DjBZ% zk$N5B4^$`n^jNSOr=t(zi8TN4fpaccsb`zOPD~iY=UEK$0Y70bG{idLx@IL)7^(pL z{??Bnu=lDeguDrd%qW1)H)H`9otsOL-f4bSu};o9OXybo6J!Lek`a4ff>*O)BDT_g z<6@SrI|C9klY(>_PfA^qai7A_)VNE4c^ZjFcE$Isp>`e5fLc)rg@8Q_d^Uk24$2bn z9#}6kZ2ZxS9<C46&Y+Q7nYM#)S{~e<-0SXbx^w1jyAP0t!{t{i)+bD@w$9YAlUQVZ z1TZ|^=9cLiz;Bipmt#c?%u(c5s;}6EMb|KG%X+!BskufNDiLAbfcJAi-eKFCylmQ6 zcLgpiYS;T5u|4vj(43@Xs-;?LT?Reu-O1voTo*8Sg!T${N!fhDdj5F-jP4kcswNTc zUPNlqr9(p*&QkY(6{Uw9+-&ZY^AVhuru!iEZSXWk{J62Y8RTWl#jvm?@UsOLN*n1U z!!2c97^PYdYbw;1W(h-dY_NJ_bbOqzz80YwLA6En%W5F}=@a-dB;!cvFG55bE7@zZ zf}Zz=u;({6%w-qMyr7YLW0H?0K>sI(RqT7?El2@B+($>eBQrNi_k#CDJ8D9}8$mmm z4oSKO^F$i+NG)-HE$O6s1--6EzJa?C{x=QgK&c=)b(Q9OVoAXYEEH20G|q$}Hue%~ zO3B^bF=t7<z$Rj(z@}-%hhp0KDg5g-Vvj!qOr85&aqTpaaojC^CwQZHKk%N1&RJ@? z3@mmU8UkLd^u+>t48sN<h@~F@WN(LX`%4J3P$~sLqIq2q^WYYan1y*WKS{^KXRSVj zlRp2YD0*vmi}GIu(VMSMj`)AFtcV!7m`T~YnAy8nxmvlKskk~@*;{;3?|-#CT^;_> zWh_zA`w~|){-!^g<vJDMm4#3w(!Hhyj3dofOB57x=Mu^T@6Gt<KN~lv>?6Mqf6ieV zFx~aPUOJGR=4{KsW7I?<=J2|lY`NTU=lt=%JE9H1vBpkcn=uq(q~=?iBt_-r(PLBM zP-0dxljJO>4W<w&)Z{UhZ0!m()I68e=px8_4B`37AI|bCZuMk_SVKAQz?8+4(l0C) z<3()qDfD9UTW*wnelf4D7bR(}=TB;gs;ds+7QE~CAQ*jDKKADDC`3G?7kn$!=a5d& z?I(JT9>q-;stY)CLB4q`-r*T$!K2o}?E-w_i>3_aEbA^MB7P5piwt1dI-6o!qWCy0 ztYy<q;G5p>!x9arGTS?kabkkyv*yxvsPQ7Vx)twkS6z2T@kZ|kb8yjm+^$|sEBm<L zGtKcNM?a1<P1GHe%USdss^9iYmKI=GuiV`dL*Z(*)<W%!5IIDyJ!oJjHJOEa1m1VQ zKco1NMHn5?h{5SRY#VFF?T!bo5_IIEbO;WfqdSQACJa+&8o3bgw;L^BimN?NlN(v) zotn;%myS`DPUIQ+7RCnB)mY`2o&e;1Xh962y`p4wurO(bDXEWXms!a&F9;L0^G^Mo zh1W&LQdXhd1KHjKV}xwOkQ>vACeqbz)RmxkkDQX-A*K!YFziuhwb|ym>C$}U|J)4y z$(z#)GH%uV6{ec%Zy~AhK|+GtG8u@c884Nq%w`O^wv2#A(&xH@c5M`Vjk*SR_tJnq z0trB#aY)!EKW_}{#L3lph5ow=@|D5Lz<fcUCo&Ka|9|4HGWHH0_J4ujUnr>JYUFD6 z7XnUeo_V0DVSIKMFD_T0AqAO|#VFDc7c?c-Q%#u00F%!_TW1@JVn<z*P@k#}SDu4q z5BK|xV6S3>sfvm@_9HKWflBOUD~)RL``-!P;(bCON_4eVdduMO>?IrQ__*zE@7(OX zUtfH@AX*53&xJW*Pu9zcqxGiM>xol0I~QL5B%Toog3Jlenc^WbVgeBvV8C8AX^Vj& z^I}H})B=VboO%q1;aU5ACMh{yK4J;xlMc`jCnZR^!~LDs_MP&8;dd@4LDWw~*>#OT zeZHwdQWS!tt5MJQI~cw|Ka^b4c|qyd<d8BjG@CVcx~A0@_+-3ySS5}V#nYxqHn&dJ z3huaTsOBL$pM0~v6%?s%@?17;o|*#UY1tt-m0po1{B8Xt+V4%@*4l_1x6MTTu=i^t zEF!^0`A{SAgixqmbf=fe`Q#RQV7q0JEE%qC5Cl7U3dvP`CnnYy>_ly(+Ql2m&AAw^ zQeSXDOOH!!mAgzAp0z)DD>6Xo``b6QwzUV@w%h}Yo>)a|xRi$jGuHQhJVA%>)PUvK zBQ!l0hq<3VZ*RnrDODP)>&iS^wf64<Gan-0fT=xEEaI^H)!ok-sB8re6ozEmX5c@6 zvzFx43)HzN8|btxEr_+m_ES??hMpoBdA+u`<Ko)3jSDsJ<bNahp^L1kFKCk01nKG# zd~B+qtlfL5f8$8ToxOxz!oqk&<wEbF*v1K2QV8d>C;MGqDvx>|p;35%6(u+IHoNbK z;Gb;TneFo<v+>*`zUKS6kwF*&b!U8e5m4YAo03a_e^!5BP42+r)LFhEy?_7U1IR<; z^0v|DhCYMSj<-;MtY%R@Fg;9Kky^pz_t2nJfKWfh5Eu@_l{^ph%1z{jkg5jQrkvD< z#vdK!nku*RrH~TdN~`wDs;d>XY1PH?O<4^U4lmA|wUW{Crrv#r%N>7k#{Gc44Fr|t z@UZP}Y-TrAmnEZ39A*@6;ccsR>)$A)S>$-Cj!=x$rz7IvjHIPM(TB+JFf{ehuIvY$ zsDAwREg*%|=>Hw$`us~RP&3{QJg%}RjJKS^mC_!U;E5u>`X`jW$}P`Mf}?7G7FX#{ zE(9u1SO;3q@ZhDL9O({-RD+SqqPX)`0l5IQu4q)49TUTkxR(czeT}4`WV~pV*KY&i zAl3~X%D2cPVD^B43*~&f%+Op)wl<&|D{;=SZwImydWL6@_RJjxP2g)s=dH)u9Npki zs~z9A+3fj0l?yu4N0^4aC5x)O<N_(0*g4u)%5Tt4@gHE>snm0qrhz@?nwG_`h(71P znbIewljU%T*cC=~NJy|)#hT+lx#^5MuDDnkaMb*Efw9eThXo|*WOQzJ*#3dmRWm@! zfuSc@#kY{Um^gBc^_Xd<M_=Opb*sV>xnl!n&y&}R4yAbK&RMc+P<gSSGsa9{ngu3h za2rxBU6lA9Q9VAy<_CQ=#9?ge+|8rFr3YI44QC0@KPf?KG3#CkaUontfvoWcA#`fT zUZ-M@9-{1Ei|?wN2X<<LG$En}QHwMqs=8ZuZNc+NsKkIl=}k#BjOIG2xpH6pY<h{d zJ7c4SQ-wCPPp+Ave;R605<i{lO4KXOUo>^Ti;YIUh|C+K<WCtgj)+#X5!{~T0amf) zA{NO!xG0_A(b+3`Y%~$@K6*;z4@GJOlO9iW_I)Uf=v75p{Zaa%riIlQ1XqxqD1P*v zC_nl;^-H^oHskLi&AkX0pf_;|=*Q=gaUudCp%zN>1|=Z^{nZ}}rxH*v{xR!i%qO~o zTr`WDE@k$M9o0r4YUFFeQO7xCu_Zgy)==;fCJ94M_rLAv&~NhfvcLWCoaGg2ao~3e zBG?Ms9B+efMkp}7BhmISGWmJsKI@a8b}4lLI48oWKY|8<gk-*;t9-{k%FCJZFy<gM z@C~rOBUWWT##Z+g3*3Vzs8fuTtjp`u#+{x*gRagQ8={zUb)t|^B2y%Lt=XH5-VU*g zu-s*8g`Ceku&#kTTsG4pdKc+Q1?Ns^+`Anuzw^Kt@dXzw8(rtBy~EfPkytdOlMc6V z+PjsVo1fq23ba`d{M8JQ|H)T-V`Ygmnsk8K`>?zuuNc$lt5Npr+<T4KxJJ<bPDeY< zV$Y5gj%daxmn&XvpKy&xAedNSRNzj*+uARZbEwx*_BW(K#OMC!{`XgH-y>p7a#sWu zh!@2nnLBVJK!$S~>r<AjX6^_+fORZ96soQxKn~@)BfuHDd$;Hq1kJ%oj=cQPA05n| zlDech7|+hqRvU>2-pN||^w|fY`CT{TFnJy`B|e5;=+_v4l8O-fkN&UQbA4NKTyntd zqK{xEKh}U{NHoQUf!M=2(&w+eef77VtYr;xs%^cPfKLObyOV_9q<<ILDt_So;x8tA z{AwHiN2#Wqm5a+41^y+oU(NG>(%76-J%vR>w9!us-0c-~Y?_EVS<!Xa#y}`2>%v!* z15s2s3eTs$Osz$JayyH|5nPAIPEX=U;r&p;K14G<1)bvn@?bM5kC{am|C5%hyxv}a z(DeSKI5ZfZ1*%dl8frIX2?);R^^~LuDOpNpk-2R8U1w92HmG1m&|j&J{EK=|p$;f9 z7Rs5|jr4r8k5El&qcuM+YRlKny%t+1CgqEWO>3;BSRZi(LA3U%Jm{@{y+A+w(gzA< z7dBq6a1sEWa4cD0W7=Ld9z0H7RI^Z7vl(bfA;72j?SWCo`#5mVC$l1Q2--%V)-uN* z9ha*s-AdfbDZ8R8*fpwjzx=WvOtmSzGFjC#X)hD%Caeo^OWjS(3h|d9_*U)l%{Ab8 z<xdQ$23|WMjf-IqBJa@-|5QJamPBg?UmANYzk#NVaoTNbS)|8H20|;zb3-A+V#wVA z0O?V!?94t>fv$yoP{OuUl@$(-sEVNt{*=qi5P=lpxWVuz2?I7Dc%BRc+NGNw+323^ z5BXGfS71oP^%apUo(Y#xkxE)y?>BFzEBZ}UBbr~R4$%b7h3iZu3S(|A;&HqBR{nK& z$;GApNnz=kNO^FL&nYcfpB7Qg;hGJPsCW44CbkG1@l9pn0`~<fs1~obTx_FSX-JYV zGQWAl6QMe=gj$TPFe4r4b4Ol;Htq0ghUXm#FhLL;q=vj^?zll8F~1Y_ME5KlGBn?W zJLZAtGO*e1y^&@oxuzM@8GNx$4<>oKy5S777uH)l{irK!ru|X+;4&0D;VE*Ii|<3P zUx#xUqvZT5kVQxsF#~MwKnv7;1pR^0;PW@$@T7I?s`_rD1EGUdSA5Q(C<>5SzE!vw z;{L&kKFM-MO>hy#-8z`sdVx})^(Dc-dw;k-h*9O2_YZw}|9^y-|8RQ`BWJUJL(Cer zP5Z@fNc>p<r+olf3Wx4QNlGzhncc!S>TXABbTRY-B5*MphpZv6#i802giwV&SkFCR zGMETyUm(KJbh+&$8X*RB#+{surjr;8^REEt`2<qz>&Dubw3$mx>|~B5IKZJ`s_6fw zKAZx9&PwBqW1Oz0r0A4GtnZd7XTKViX2%kPfv+^X3|_}RrQ2e3l<T~g*|IE{P97HV zvf#Y<i{KPN_dP%1)NHb~ix&=&GH9>=KG_VyY`H?I5&CS+lAX5HbA%TD9u6&s#v!G> zzW9n4J%d5ye7x0y`*{KZvqyXUfMEE^ZIffzI=Hh|3J}^yx7eL=s+TPH(Q2GT-sJ~3 zI463C{(ag7-hS1ETtU;_&+49ABt5!A7C<XW?{o=2DnJxLDD~{m*zq$azI0t7>wLwe z=SoA8mYZIQeU;9txI=zcQVbuO%q@E)JI+6Q!3lMc=Gbj(ASg-<Uq;hB9d^p}DAXc~ zT?U|Ep>{V27u>z2e8n;Nc*pf}AqKz1D>p9G#QA+7mqqrEjGfw+85Uyh!=tTFTv3|O z+)-kFe_8FF_EkTw!YzwK^Hi^_dV5x-Ob*UWmD-})qKj9@aE8g240nUh=g|j28^?v7 zHRTBo{0KGaWBbyX2+lx$wgXW{3aUab6B<q-FjF>hm1G1{jTC7ota*JM6t+qy)c5<@ zpc&<Cv-}2TvNf)-u^)w4IR#IAb30P8NKX2F^|M`)t)gNvmzY$92){_sASc~#MG?G6 z01+~17JwM!JPSxaJJtTz7$&8s`H3FldxQ%9@~nj<<O#kvf=K=$4nLLmHGiFo3Mq&* ziIi#gQw#(**q&>(jVdTJf(q3xB=JotgF$X>cxh7k*(T`-V~AR+`%e?YOeALQ2Qud( zz35YizXt(aW3qndR}fTw1p()Ol4t!D1pitGNL95{SX4ywzh0SF;=!wf=?Q?_h6!f* zh7<+GFi)q|XBsvXZ^qVCY$LUa{5?!CgwY?EG;*)0ceFe&=A;!~o`ae}Z+6me#^sv- z<kA1n(=XTnu@rJsCenhu-Zv&%WBDK;wE+-m5)3gqDM=UJSV|IgE?>1F6=WNd6>M(~ z+092z>?Clrcp)lYNQl9jN-JF6n&Y0mp7|I0dpPx+4*RRK+VQI~>en0Dc;Zf<!>l+x z_e_b7s`t1_A`RP3$H}y7F9_na%D7EM+**G_Z0l_nwE+&d_kc35n$Fxkd4r=ltRZhh zr9zER8>j(EdV&Jgh(+i}ltESBK62m0nGH6tCBr90!4)-`HeBmz54p~QP#dsu%nb~W z7sS|(Iydi>C@6ZM(Us!jyIiszMkd)^u<1D+R@~O>HqZIW&kearPWmT>63%_t2B{_G zX{&a(gOYJx!Hq=!T$RZ&<8LDnxsmx9+TBL0gTk$|vz9O5GkK_Yx+55^R=2g!K}NJ3 zW?C;XQCHZl7H`K5^BF!Q5X2^Mj93&0l_O3Ea3!Ave|ixx+~bS@Iv18v2ctpSt4zO{ zp#7pj!AtDmti$T`e9{s^jf(ku&E|83JIJO5Qo9weT6g?@vX!{7)cNwymo1+u(YQ94 zopuz-L@|5=h8A!(g-<F;G9^=CwUG2BBM&6@esQFH4>MXgLJC0MA|CgQF8qlonnu#j z;uCeq9ny9QSD|p)9sp3ebgY3rk#y<wu$Scub#>0DA(SHdh$DUm^?GI<>%e1?&}w(b zd<n{_{wZL^#}W>ip1;P2Z=1wM+$q=TgLP$}svd!vk+BZ@h<^4R=GS2+sri7Z*2f`9 z5_?i)xj?m#pSVchk-SR!2&uNhzEi+#5t1Z$o0PoLGz*pT64%+|Wa+rd5Z}60(j?X= z{NLjtgRb|W?CUADqOS@(*MA-l|E342NxRaxLTDqsO<GMIr8u8#%dIQrz(r`Q(hkza zil8N-`Js{wU0Gy<JdGKt>yfWWe%N(jjBh}G<qND?0TH2WotV2BO}oGFXR`nNIoZPu zAYBqht4AIf6%UvOQWL(@v@#P!g?Z{m=yxdflhU-MrdJ3Lu4OwZ%yKkuPkk0$Ko)O* z;5yrsNkvYZsjZQILNsEr+ECa0P<^XyVVf2;%`lxDRkz-!;wa1;EB{emo`C=%{Gykq zq<4i~ETk#P9zK#gq4PdG1l$Vspzwyb@<LIRCp@UiYQvSVfg*oiL+eCZD0<3etyAQ> zm7WPel6jXijaTiNita+z(5GCO0NM=Melxud57P<u@R2P46Q9-DyjXBHUN>P^d_U## zbA;9iVi<@wr0DGB8<n8`yw;2Kv**CeqAs$L&plPhIa#v7(dTNoPt@&}ED@M*lxC!x z`6s~+J|uy;3o7Lq<uMmSEF9Dw$gP)!=7bwIZF}v$SuOexM&6SRtdGcL+`+Tm+leuz zpp$tX{Sz|>=T9Ab#2K_#zi=<XArhO6r_`n&7XSM212-MzWyRNG*!uO-#ecnE^8eXw z{A)4%t2FvosVP<UQ~s;l`0?z0m3m-lgN!65Mz=sfFM<3$$g-N5nIt_Q>$igy<I%16 z>K48@;V|W`fg~7;+!q8)aCOo{HA@vpSy-4`^!ze6-~8|QE||hC{ICKllG9fbg_Y7v z$jn{00!ob3!@~-Z%!rSZ0JO#@>|3k10mLK0JR<I1S>KP-Cc8UYFu>z93=Ab-r^oL2 zl`-&VBh#=-?{l1TatC;VweM^=M7-DUE>m+xO7Xi6vTEsReyLs8KJ+2GZ&rxw$d4IT zPXy6pu^4#e;;ZTsgmG+ZPx>piodegkx2n0}SM77+Y*j^~ICvp#2wj^BuqRY*&cjmL zcKp78aZt>e{3YBb4!J_2|K~A`lN=u&5j!byw`1itV(+Q_?RvV7&Z5XS1HF)L2v6ji z&kOEPmv+k_lSXb{$)of~(BkO^py&7oOzpjdG>vI1kcm_oPFHy38%D4&A4h_CSo#lX z2#oqMCTEP7UvUR3mwkPxbl8AMW(e{ARi@HCYLPSHE^L<1I}OgZD{I#YH#GKnpRmW3 z2jkz~Sa(D)f?V?$gNi?<F$5NpPo_(+mLu%j0uVGhEpW~}8A-6p@(iN<J78jy&84)} zW71~;kMKbRG+MZ(!>6)Y;Sm{&?~2p=0&BUl_(@hYeX8YjaRO=IqO7neK0RsSNdYjD zaw$g2sG(>JR=8Iz1<iqC50Fc?zkwnhu-?J#4v?gbo)h!toq+!EipMj&Dd=4)`^!2@ zL(!GW5QxLJO&{?1u~Q}Au)moY@9Q-~Yr01D0la`rUI3jK%5PxGU7;z+IlI=Bb;^2b zL|Kc&B2+#W3&e}l>SK4`*kqd_3-?;_BIcaaMd^}<@MYbYisWZm2C2<aQM85hCqTrH z{L!?Z_;my2c?%RMej)yS*$eqpa!UR3e9te>|Np_l|8r9yM|JkUngSo@?wci(7&O9a z%|V(4C1c9pps0xxzPbXH=}QTxc2rr7fXk$9`a6TbWKPCz&p=VsB8^W96W=BsB|7bc zf(QR8&Ktj*iz)wK&mW`#V%4XTM&jWNnDF56O+2bo<3|NyUhQ%#OZE8$Uv2a@J>D%t zMVMiHh?es!Ex19q&6eC&L=XDU_BA&uR^^w>fpz2_`U87q_?N2y;!Z!bjoeKrzfC)} z?m^PM=(z{%n9K`p|7Bz$LuC7!>tFOuN74MFELm}OD9?%jpT>38J;=1Y-VWtZAscaI z_8jUZ#GwWz{JqvGEUmL?G#l5E=*m>`cY?m*XOc*yOCNtpuIGD+Z|kn4Xww=BLrNYS zGO=wQh}Gtr|7DGXLF%|`G>J~l{k^*{;S-Zhq|&HO7rC_r;o`gTB7)uMZ|WWIn@e0( zX$MccUMv3ABg^$%_lNrgU{EVi8O^UyGHPNRt%R!1#MQJn41aD|_93NsBQhP80yP<9 zG4(&0u7AtJJXLPcqzjv`S~5;Q|5TVGccN=Uzm}K{v)?f7W!230C<``9(64}D2raRU zAW5bp%}VEo{4Rko`bD%Ehf=0voW?-4Mk#d3_pXTF!-TyIt6U+({6OXWVAa;s-`Ta5 zTqx&8msH3+DLrVmQOTBOAj=uoxKYT3DS1^zBXM?1W+7gI!aQNPYfUl{3;PzS9*F7g zWJN8x?KjBDx^V&6iCY8o_gslO16=kh(|Gp)kz8qlQ`dzxQv;)V&t+B}wwdi~uBs4? zu~G|}y!`3;8#vIMUdyC7YEx6bb^1o}G!Jky4cN?BV9ejBfN<&!4M)L&lRKiuMS#3} z_B}Nkv+zzxhy{dYCW$oGC&J(Ty&7%=5B$sD0bkuPmj7g>|962`(Q{ZZMDv%YMuT<n z1<0L@A~^*&C~fETTawHVh1kk4b*^p0vQ^7?+3dKBe<pM8Snh`k_7R%#IZRUEl1U~% z`#y5ddd+xk?tVQb4dNJ(7Ry%2!BTF1HzW?PK!2%Oj>^Kwe<oH3RpEUQV(1=JAftKZ zy};jv^`iGA^yoK}($W9zl~UM?CzovcbP5)_-K0QR<B0^>iRDvYTEop3IgFv#)(w>1 zSzH><Zx#DBcM*ETggCrIL|G$?#sL+^<gVn#xwx<>J`q!LK)c(AK>&Ib)A{g`<Y-)} z(@A>Fdykxqd`Yq@yB}E{gnQV$K!}RsgMGWqC3DKE(=!{}ekB3+(1?g}xF>^icEJbc z5bdxAPkW90atZT+&*7qoLqL#p=>t-(-lsnl2XMpZcYeW|o|a322&)yO_8p(&Sw{|b zn(tY$xn5yS$DD)UYS%sP?c|z>1dp!QUD)l;aW#`%qMtQJjE!s2z`+bTSZmLK7SvCR z=@I4|U^sCwZLQSfd*ACw9B@`1c1|&i^W_OD(570SDLK`MD0wTiR8|$7+%{cF&){$G zU~|$^Ed?TIxyw{1$e|D$050n8AjJvvOWhLtLHbSB|HIfhMpqVf>AF&}ZQHhOJ14Bz zww+XL+qP}nww+W`F>b!by|=&a(cM4JIDhsTXY8@|ntQG}-}jm0&Bcj|LV(#sc=BNS zRjh<Mlkf>;k9l>EdAFdd)=H!U`~$WP*}~^3HZ_?H>gKw>NB<D?df$IC%55Zl`EPwc zRF>a;tA8M1{>St|)yDF_=~{KEPAGkg3VB`QCHol!AQ0|?e^W?81f{@()Wy!vQ$bY; z0ctx)l<l3Egk{Ob>7VK83d6;dp!s{Nu=SwXZ8lHQHC*J2g@P0a={B8qHd<!Rx=U=y zZhU*Z!GA%uunxv9&4$#mX+|}S)urtQN=7La7qnsxu>v(+O3wV=4-t4HK1+smO#=S; z3cSI#Nh+N@AqM#6wPqjDmQM|x95<n5VlzgWRH&oDW?c}DT^%?B8C0l+B0<BSKyNf1 z@50z}-d3zrSn&7`r1tBSp<zb3^nhH#XuDC?R<KtB*VsyKR`dRh)&DkLIrq4o!?;Lk zondptVSwpbOiowRa-P*4A7o%#IYH#y*MPqzE9G%OcE;(l=a5Gbdc^<iHA{4$gMK2y zrcQ~;DrQl(Xod1}HF3{_dN{dd)Iq**zG_<1@e+8Q8+Oq;jgidKOGIuhBe_rBN^N(^ zH&yrkQqs47d>JG|l1<sF7&JuwXR&1!7b?5$CbRqF7%}I8mpCr(sj;K7IQl+Ud)#bZ zp7IC+SbpjPV~m#KY)1CSNeLmt63WJp#VvwlYf+=uB{p=aUnI`+`Y>#sAU|>I6<Rxv z+8ksxQP-bXJt|;JqZ0=Syg@fkr7?v9z=bM6Vn&}>NdF*G@bD?1t|ytHlkKD+z9}#j zbU+x_cR-j9yX4s{_y>@zk*ElG1yS({BInGJcIT>l4N-DUs6fufF#GlF2lVUNOAhJT zGZThq54GhwCG(h4?yWR&Ax8hU<*U)<g>?g+HY5-@{#ls5CVV(Wc>Bavs|l<}U|hZn z_%m+5i_gaakS*Pk7!v&w3&?R5Xb|AkCdytTY;r+Z7f#Id=q+W8cn)*9tEet=OG+Y} z58U&!%t9gYMx2N=8F?gZhIjtkH!`E*XrVJ?$2rRxLhV1z82QX~PZi8^N5z6~f-MUE zLKxnNoPc-SGl7{|Oh?ZM$jq67sSa)Wr&3)0YxlJt(vKf!-^L)a|HaPv*IYXb;QmWx zsqM>qY;tpK3RH-omtta+Xf2Qeu^$VKRq7`e$N-UCe1_2|1F{L3&}M0XbJ@^xRe&>P zRdKTgD6601x#fkDWkoYzRkxbn#*>${dX+UQ;FbGnTE-+kBJ9KPn)501#_L4O_k`P3 zm+$jI{|EC?8BXJY{P~^f-{**E53k%kVO$%p+=H5DiIdwMmUo>2euq0UzU90FWL!>; z{5@sd0ecqo5j!6AH@g6Mf3keTP$PFztq}@)^ZjK;H6Go$#SV2|2bAFI0%?aXgVH$t zb4Kl`$Xh8q<G488u@$4lX!B=3?g=wlC?}MC;F?H%YQrVNOwB#z7-f_|Wz?O!b4I~2 z^Qw&0hykWBc$}5NngS)c1*7`tH73!7vUHgRMs>LrMbZUS<2*7^F0^?lrOE=$DHW+O zvLdczsu0^TlA6RhDy3=@s!k^1D~Awulk!Iyo#}W$xq8{yTAK!CLl={H0@YGhg-g~+ z(u>pss4k#%8{J%~%8=H5!T`rqK6w^es-cNVE}=*lP^`i&K4R=peg1tdmT~UAbDKc& zg%Y*1E<jNK6bVo^5$q7Be!g@_B}<2f!MazAse=SHXka44U?M8cg8{iRQqX625kGny zEx>{hBf<)xO>HDWV7BaMWX6FW4ou1T2m^6{Jb!Su1UaCCYY8RR8hAV$7ho|FyEyP~ zEgK`<ybDN}WQ7ppf~i48Sp+j=w6UI16W6MuJXhL6VlQ|!lSyz6m|Gs@>@%a$-C2`p zV*~G>GOAs*3KN;~IY_UR$ISJxB(N~K>=2C2V6>xTmuX4<wHTgMVWGBYU0G4B(`;}2 zw_J6Ct{nL}*%nG0uk<t$To_fcVQEvXjtQYeWv?v&5m9S(NJkQnc)rvU7`Je&48A!8 z_->klRXdrJd&UPAw7&|KEwF8Zcy2j-*({gSNR1^p02Oj88GN9a_Hq;Skdp}kO0;<y ztR-y<(h)MzSR8PG`MEz?T1Lf{zq~R3i)I#s$y{Wn^A`t(9>FLbje%2ZvPiltDZgv^ z#pb4&m^!79;O8F+Wr9X71laPY!CdNXG?J6C9KvdAE2xWW1>U~3;0v≫L+crb^Bz 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 E0aA=+Q2+n{ literal 0 HcmV?d00001 diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..0d23ac00c --- /dev/null +++ b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/powertools-examples-core/gradle/gradlew b/examples/powertools-examples-core/gradle/gradlew new file mode 100755 index 000000000..fcb6fca14 --- /dev/null +++ b/examples/powertools-examples-core/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/gradle/gradlew.bat b/examples/powertools-examples-core/gradle/gradlew.bat new file mode 100644 index 000000000..6689b85be --- /dev/null +++ b/examples/powertools-examples-core/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/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java new file mode 100644 index 000000000..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..af3ec1275 --- /dev/null +++ b/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java @@ -0,0 +1,24 @@ +package helloworld; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class AppTest { + @Test + public void successfulResponse() { + App app = new App(); + APIGatewayProxyResponseEvent result = app.handleRequest(null, null); + assertEquals(200, result.getStatusCode().intValue()); + assertEquals("application/json", result.getHeaders().get("Content-Type")); + 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/gradle/template.yaml b/examples/powertools-examples-core/gradle/template.yaml new file mode 100644 index 000000000..a717c2998 --- /dev/null +++ b/examples/powertools-examples-core/gradle/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: java8 + 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: java8 + 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 From e35de90f28072814eeb9e6bcb5286c38b8c0338e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:07:00 +0200 Subject: [PATCH 0456/1008] maven deploy configuration (skip in tests and examples) (#1388) --- examples/pom.xml | 16 +- examples/powertools-examples-batch/pom.xml | 9 +- .../pom.xml | 9 +- .../powertools-examples-core/cdk/app/pom.xml | 9 +- .../cdk/infra/pom.xml | 9 +- examples/powertools-examples-core/sam/pom.xml | 144 +++++++------ .../powertools-examples-idempotency/pom.xml | 202 +++++++++--------- .../powertools-examples-parameters/pom.xml | 75 ++++--- .../powertools-examples-serialization/pom.xml | 9 +- examples/powertools-examples-sqs/pom.xml | 134 ++++++------ .../powertools-examples-validation/pom.xml | 73 ++++--- powertools-e2e-tests/pom.xml | 11 +- powertools-test-suite/pom.xml | 12 +- 13 files changed, 401 insertions(+), 311 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 948005546..eae9e10e5 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -41,9 +41,17 @@ <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> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> </project> \ No newline at end of file diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 9faa4dc24..290fe0cd2 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,6 @@ <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.133</sdk.version> </properties> @@ -120,6 +119,14 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 20aa5aeaf..030998631 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -13,7 +13,6 @@ <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.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> <aws.sdk.version>2.20.133</aws.sdk.version> @@ -145,6 +144,14 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index a1aba1b8d..7831df839 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -13,7 +13,6 @@ <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> @@ -120,6 +119,14 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 5131e22af..cdaccedf4 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -10,7 +10,6 @@ <cdk.version>2.92.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> - <maven.deploy.skip>true</maven.deploy.skip> </properties> <build> <plugins> @@ -31,6 +30,14 @@ <mainClass>cdk.CdkApp</mainClass> </configuration> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <dependencies> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 7350542cd..09cab6cd6 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -1,19 +1,18 @@ <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.18.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> @@ -38,9 +37,9 @@ <version>1.2.3</version> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -54,72 +53,81 @@ </dependency> <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> + <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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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="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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the examples --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index e9337aafc..1afce1f29 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -13,9 +13,9 @@ --> <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.18.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> @@ -26,7 +26,6 @@ <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> @@ -51,9 +50,9 @@ <version>1.2.3</version> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -92,97 +91,106 @@ </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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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="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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 301247136..fe74f859d 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -1,5 +1,5 @@ <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.18.0-SNAPSHOT</version> @@ -10,9 +10,8 @@ <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> @@ -57,37 +56,45 @@ </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> + <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> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 18318ff76..7b63d266d 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -10,7 +10,6 @@ <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> @@ -64,6 +63,14 @@ <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> <version>3.1.2</version> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index b030d22fd..caee0a44d 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -1,5 +1,5 @@ <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.18.0-SNAPSHOT</version> @@ -11,7 +11,6 @@ <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> @@ -36,9 +35,9 @@ <version>1.2.3</version> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -57,68 +56,77 @@ </dependency> <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> + <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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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="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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 3aa8dad2b..29f52b33e 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -13,7 +13,7 @@ --> <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.18.0-SNAPSHOT</version> @@ -24,7 +24,6 @@ <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> @@ -66,37 +65,45 @@ </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> + <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> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8fd96030b..134b71a06 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -32,9 +32,6 @@ <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.69</constructs.version> <cdk.version>2.92.0</cdk.version> - - <!-- Don't deploy the e2e tests --> - <maven.deploy.skip>true</maven.deploy.skip> </properties> <dependencies> @@ -183,6 +180,14 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> </plugin> + <!-- Don't deploy the e2e tests --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 80b8635f5..55e713ec0 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -47,10 +47,6 @@ </developer> </developers> - <properties> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> - <distributionManagement> <snapshotRepository> <id>ossrh</id> @@ -159,6 +155,14 @@ </execution> </executions> </plugin> + <!-- Don't deploy tests --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> </project> \ No newline at end of file From 34c6793b5043d09458af88653ba52401c880f92d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:17:46 +0200 Subject: [PATCH 0457/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1394) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.92.0 to 2.93.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.92.0...v2.93.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index cdaccedf4..6facf8a46 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>1.18.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.92.0</cdk.version> + <cdk.version>2.93.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 134b71a06..d3acde3ea 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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.92.0</cdk.version> + <cdk.version>2.93.0</cdk.version> </properties> <dependencies> From c21d66d3ba2c2adca4b4a2e90a135e1eac6b763c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 13:31:55 +0200 Subject: [PATCH 0458/1008] build(deps): bump aws.sdk.version from 2.20.133 to 2.20.134 (#1396) Bumps `aws.sdk.version` from 2.20.133 to 2.20.134. Updates `software.amazon.awssdk:bom` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:http-client-spi` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:url-connection-client` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:sqs` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:s3` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:dynamodb` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:lambda` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:kinesis` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:cloudwatch` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:xray` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:cloudformation` from 2.20.133 to 2.20.134 Updates `software.amazon.awssdk:sts` from 2.20.133 to 2.20.134 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 030998631..ad84d19a1 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.133</aws.sdk.version> + <aws.sdk.version>2.20.134</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index caee0a44d..ac0e577ed 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.133</version> + <version>2.20.134</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 8226fa878..9a9b8f05d 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.133</aws.sdk.version> + <aws.sdk.version>2.20.134</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 9f33fed2f62e327f30b38632851ddf891939c9c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 14:18:03 +0200 Subject: [PATCH 0459/1008] build(deps): bump aws.sdk.version from 2.20.134 to 2.20.135 (#1398) Bumps `aws.sdk.version` from 2.20.134 to 2.20.135. Updates `software.amazon.awssdk:bom` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:http-client-spi` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:url-connection-client` from 2.20.133 to 2.20.135 Updates `software.amazon.awssdk:sqs` from 2.20.133 to 2.20.135 Updates `software.amazon.awssdk:s3` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:dynamodb` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:lambda` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:kinesis` from 2.20.133 to 2.20.135 Updates `software.amazon.awssdk:cloudwatch` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:xray` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:cloudformation` from 2.20.134 to 2.20.135 Updates `software.amazon.awssdk:sts` from 2.20.134 to 2.20.135 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ad84d19a1..127c78e66 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.134</aws.sdk.version> + <aws.sdk.version>2.20.135</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index ac0e577ed..9692649ec 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.134</version> + <version>2.20.135</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 9a9b8f05d..273a4b4a4 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.134</aws.sdk.version> + <aws.sdk.version>2.20.135</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c6932aeb0d0d5689f1c8a5d0f3897c8c69850c3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 14:09:54 +0200 Subject: [PATCH 0460/1008] build(deps): bump aws.sdk.version from 2.20.135 to 2.20.136 (#1404) Bumps `aws.sdk.version` from 2.20.135 to 2.20.136. Updates `software.amazon.awssdk:bom` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:http-client-spi` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:url-connection-client` from 2.20.133 to 2.20.136 Updates `software.amazon.awssdk:sqs` from 2.20.133 to 2.20.136 Updates `software.amazon.awssdk:s3` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:dynamodb` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:lambda` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:kinesis` from 2.20.133 to 2.20.136 Updates `software.amazon.awssdk:cloudwatch` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:xray` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:cloudformation` from 2.20.135 to 2.20.136 Updates `software.amazon.awssdk:sts` from 2.20.135 to 2.20.136 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 127c78e66..1273d8d15 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.135</aws.sdk.version> + <aws.sdk.version>2.20.136</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 9692649ec..b2ef7145c 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.135</version> + <version>2.20.136</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 273a4b4a4..9066d5880 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.135</aws.sdk.version> + <aws.sdk.version>2.20.136</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From b5135c9a3b1557b3aa7e10728fd1fe95ac6605dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:57:43 +0200 Subject: [PATCH 0461/1008] chore: update v2 (#1409) --- .github/workflows/auto-merge.yml | 8 +- .github/workflows/build-docs.yml | 4 +- .github/workflows/build.yml | 13 +- .github/workflows/dispatch_analytics.yml | 2 +- .github/workflows/docs.yml | 4 +- .github/workflows/publish.yml | 9 +- .github/workflows/release-drafter.yml | 2 +- .github/workflows/release-prep.yml | 16 +- .github/workflows/run-e2e-tests.yml | 6 +- .github/workflows/secure_workflows.yml | 32 +++ .github/workflows/spotbugs.yml | 6 +- .gitignore | 2 + README.md | 18 +- docs/index.md | 17 +- examples/pom.xml | 16 +- examples/powertools-examples-batch/pom.xml | 11 +- .../pom.xml | 11 +- .../README.md | 5 + .../cdk/app/pom.xml | 11 +- .../cdk/infra/pom.xml | 13 +- .../gradle/README.md | 38 +++ .../gradle/build.gradle | 35 +++ .../gradle/events/event.json | 62 +++++ .../gradle/gradle/wrapper/.gitignore | 2 + .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../gradle/gradlew | 248 ++++++++++++++++++ .../gradle/gradlew.bat | 92 +++++++ .../gradle/src/main/java/helloworld/App.java | 107 ++++++++ .../src/main/java/helloworld/AppStream.java | 38 +++ .../src/test/java/helloworld/AppTest.java | 24 ++ .../gradle/template.yaml | 71 +++++ .../sam/pom.xml | 142 +++++----- .../powertools-examples-idempotency/pom.xml | 202 +++++++------- .../powertools-examples-parameters/pom.xml | 75 +++--- .../powertools-examples-serialization/pom.xml | 9 +- .../powertools-examples-validation/pom.xml | 73 +++--- pom.xml | 2 +- powertools-e2e-tests/pom.xml | 13 +- 39 files changed, 1161 insertions(+), 285 deletions(-) create mode 100644 .github/workflows/secure_workflows.yml create mode 100644 examples/powertools-examples-core-utilities/gradle/README.md create mode 100644 examples/powertools-examples-core-utilities/gradle/build.gradle create mode 100644 examples/powertools-examples-core-utilities/gradle/events/event.json create mode 100644 examples/powertools-examples-core-utilities/gradle/gradle/wrapper/.gitignore create mode 100644 examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/powertools-examples-core-utilities/gradle/gradlew create mode 100644 examples/powertools-examples-core-utilities/gradle/gradlew.bat create mode 100644 examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core-utilities/gradle/src/test/java/helloworld/AppTest.java create mode 100644 examples/powertools-examples-core-utilities/gradle/template.yaml diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 21b5e20e5..5401eedc9 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -17,12 +17,12 @@ jobs: 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 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: ahmadnassri/action-workflow-run-wait@2aa3d9e1a12ecaaa9908e368eaf2123bb084323e # v1.4.4 with: timeout: 300000 - name: 'Download artifact' - uses: actions/github-script@v3.1.0 + uses: actions/github-script@47f7cf65b5ced0830a325f705cad64f2f58dddf7 # v3.1.0 with: script: | var artifacts = await github.actions.listWorkflowRunArtifacts({ @@ -43,7 +43,7 @@ jobs: fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - run: unzip pr.zip - name: Create review - uses: actions/github-script@v3 + uses: actions/github-script@47f7cf65b5ced0830a325f705cad64f2f58dddf7 # v3.1.0 with: script: | var fs = require('fs'); diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index f4a9c4d3f..a4ab6e7de 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -22,9 +22,9 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 with: python-version: "3.8" - name: Capture branch and tag diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ed871a6c..2ded092ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,17 +54,22 @@ jobs: JAVA: ${{ matrix.java }} AWS_REGION: eu-west-1 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} cache: 'maven' - name: Build with Maven run: mvn -B install --file pom.xml + - name: Build Gradle Example + if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 + run: | + cd examples/powertools-examples-core-utilities/gradle + ./gradlew build - name: Upload coverage to Codecov - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # 3.1.1 + uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.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 @@ -78,7 +83,7 @@ jobs: mkdir -p ./pr echo ${{ github.event.number }} echo ${{ github.event.number }} > ./pr/NR - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 name: Upload artifact with: name: pr diff --git a/.github/workflows/dispatch_analytics.yml b/.github/workflows/dispatch_analytics.yml index 49a276f6f..c93cb5b36 100644 --- a/.github/workflows/dispatch_analytics.yml +++ b/.github/workflows/dispatch_analytics.yml @@ -29,7 +29,7 @@ jobs: environment: analytics steps: - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: aws-region: eu-central-1 role-to-assume: ${{ secrets.AWS_ANALYTICS_ROLE_ARN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9c09e294d..5e37c5f45 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,9 +16,9 @@ jobs: runs-on: ubuntu-latest environment: Docs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 with: python-version: "3.8" - name: Capture branch and tag diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2068c09c5..03f04e0f4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,15 +8,16 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Set up Maven Central Repository - uses: actions/setup-java@v2 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: - distribution: 'zulu' + distribution: 'corretto' java-version: 8 server-id: ossrh server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD + # TODO: use environments https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment 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 @@ -30,7 +31,7 @@ jobs: MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Close issues related to this release - uses: actions/github-script@v5 + uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 with: script: | const post_release = require('.github/workflows/post_release.js') diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 5b27fd671..72bd5c24f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -10,6 +10,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@569eb7ee3a85817ab916c8f8ff03a5bd96c9c83e # v5.23.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml index 345bd2a10..f7a3c74c0 100644 --- a/.github/workflows/release-prep.yml +++ b/.github/workflows/release-prep.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Get current date id: date run: echo "::set-output name=date::$(date +'%Y-%m-%d')" @@ -18,42 +18,42 @@ jobs: 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 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 + uses: jacobtomlinson/gha-find-replace@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 with: find: '## [Unreleased]' replace: | @@ -66,7 +66,7 @@ jobs: regex: false include: CHANGELOG.md - name: Create Release Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@18f7dc018cc2cd597073088f7c7591b9d1c02672 # v3.14.0 with: commit-message: chore:prep release ${{ github.event.inputs.targetRelease }} token: ${{ secrets.RELEASE }} diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 36c9dd97a..c4a8c6fb2 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -41,15 +41,15 @@ jobs: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1.6.1 + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} diff --git a/.github/workflows/secure_workflows.yml b/.github/workflows/secure_workflows.yml new file mode 100644 index 000000000..1430e91d6 --- /dev/null +++ b/.github/workflows/secure_workflows.yml @@ -0,0 +1,32 @@ +name: Lockdown untrusted workflows + +# PROCESS +# +# 1. Scans for any external GitHub Action being used without version pinning (@<commit-sha> vs @v3) +# 2. Scans for insecure practices for inline bash scripts (shellcheck) +# 3. Fail CI and prevent PRs to be merged if any malpractice is found + +# USAGE +# +# Always triggered on new PR, PR changes and PR merge. + + +on: + push: + paths: + - ".github/workflows/**" + pull_request: + paths: + - ".github/workflows/**" + +jobs: + enforce_pinned_workflows: + name: Harden Security + runs-on: ubuntu-latest + permissions: + contents: read # checkout code and subsequently GitHub action workflows + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Ensure 3rd party workflows have SHA pinned + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@555a30da2656b4a7cf47b107800bef097723363e # v2.1.3 diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 0b07bcd81..d314107fa 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -23,11 +23,11 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java JDK 1.8 - uses: actions/setup-java@v2 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: - distribution: 'zulu' + distribution: 'corretto' 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/ diff --git a/.gitignore b/.gitignore index 04e85211d..b404d2cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ example/HelloWorldFunction/.gradle example/HelloWorldFunction/build /example/.gradle/ /example/.java-version +.gradle +build/ \ No newline at end of file diff --git a/README.md b/README.md index 76f8b2e4b..ed3f86e77 100644 --- a/README.md +++ b/README.md @@ -132,11 +132,18 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam <summary><b>Gradle - Java 11+</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() } @@ -145,6 +152,7 @@ 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 "org.aspectj:aspectjrt:1.9.8.RC3" } sourceCompatibility = 11 @@ -161,6 +169,12 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' } + // the freefair aspect plugins targets gradle 7.6.1 + // https://docs.freefair.io/gradle-plugins/6.6.3/reference/ + wrapper { + gradleVersion = "7.6.1" + } + repositories { mavenCentral() } @@ -178,7 +192,7 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ## 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/v1.17.0/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). diff --git a/docs/index.md b/docs/index.md index d3e487174..6af4c5e8d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -211,10 +211,17 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "Gradle Java 11+" ```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() @@ -233,10 +240,18 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "Gradle Java 1.8" ```groovy + plugins { id 'java' id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' } + + // the freefair aspect plugins targets gradle 7.6.1 + // https://docs.freefair.io/gradle-plugins/6.6.3/reference/ + wrapper { + gradleVersion = "7.6.1" + } + repositories { mavenCentral() diff --git a/examples/pom.xml b/examples/pom.xml index b790cb12e..da6e272de 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -40,9 +40,17 @@ <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> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> </project> \ No newline at end of file diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 9efa7fa63..cfc035238 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,8 +14,7 @@ <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.128</sdk.version> + <sdk.version>2.20.133</sdk.version> </properties> <dependencies> @@ -120,6 +119,14 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 50cfff85d..85e330742 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -13,10 +13,9 @@ <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.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.130</aws.sdk.version> + <aws.sdk.version>2.20.136</aws.sdk.version> </properties> <dependencyManagement> <dependencies> @@ -145,6 +144,14 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/examples/powertools-examples-core-utilities/README.md b/examples/powertools-examples-core-utilities/README.md index f11982477..6960bb1f5 100644 --- a/examples/powertools-examples-core-utilities/README.md +++ b/examples/powertools-examples-core-utilities/README.md @@ -6,9 +6,14 @@ This project demonstrates the Lambda for Powertools Java module - including [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/) +We also provide an example showing the integration of SAM, Powertools, and Gradle: + +* [AWS SAM with a Gradle build](../powertools-examples-core-utilities/gradle/) + 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. diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 6895f4117..76e02ea48 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.1</version> + <version>1.17.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> @@ -13,7 +13,6 @@ <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> @@ -120,6 +119,14 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index d81118cde..d67077fae 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,13 +4,12 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.91.0</cdk.version> + <cdk.version>2.93.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> - <maven.deploy.skip>true</maven.deploy.skip> </properties> <build> <plugins> @@ -31,6 +30,14 @@ <mainClass>cdk.CdkApp</mainClass> </configuration> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> <dependencies> 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..cd6107151 --- /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 1.8 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](../../powertools-examples-core/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..e7367c246 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -0,0 +1,35 @@ + +plugins { + id 'java' + id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" +} + +wrapper { + gradleVersion = "7.6.1" +} + +compileJava { + sourceCompatibility = "1.8" + targetCompatibility = "1.8" + + ajc { + enabled = true + } +} + +repositories { + 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.11.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' + aspect 'software.amazon.lambda:powertools-tracing:1.17.0' + aspect 'software.amazon.lambda:powertools-logging:1.17.0' + aspect 'software.amazon.lambda:powertools-metrics:1.17.0' + testImplementation 'junit:junit:4.13.2' +} + 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 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340 GIT binary patch literal 63375 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≫L+crb^Bz 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 E0aA=+Q2+n{ literal 0 HcmV?d00001 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..0d23ac00c --- /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-7.3.3-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..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core-utilities/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core-utilities/gradle/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..af3ec1275 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/src/test/java/helloworld/AppTest.java @@ -0,0 +1,24 @@ +package helloworld; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class AppTest { + @Test + public void successfulResponse() { + App app = new App(); + APIGatewayProxyResponseEvent result = app.handleRequest(null, null); + assertEquals(200, result.getStatusCode().intValue()); + assertEquals("application/json", result.getHeaders().get("Content-Type")); + 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-utilities/gradle/template.yaml b/examples/powertools-examples-core-utilities/gradle/template.yaml new file mode 100644 index 000000000..a717c2998 --- /dev/null +++ b/examples/powertools-examples-core-utilities/gradle/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: java8 + 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: java8 + 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/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 204a828c0..a85bf8941 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> - <artifactId>powertools-examples-core-utilities</artifactId> + <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics)</name> @@ -13,7 +13,6 @@ <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> @@ -38,9 +37,9 @@ <version>1.2.3</version> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -54,72 +53,81 @@ </dependency> <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> + <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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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="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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index d0e98ec28..b7b710ac1 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -13,9 +13,9 @@ --> <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>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> @@ -26,7 +26,6 @@ <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> @@ -51,9 +50,9 @@ <version>1.2.3</version> </dependency> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -92,97 +91,106 @@ </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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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="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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 19be585a5..7bf354ac1 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -1,5 +1,5 @@ <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>2.0.0-SNAPSHOT</version> @@ -10,9 +10,8 @@ <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> @@ -57,37 +56,45 @@ </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> + <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> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index d084df983..13f01a8d3 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -10,7 +10,6 @@ <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> @@ -64,6 +63,14 @@ <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> <version>3.1.2</version> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index d4b26d166..296504a32 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -13,7 +13,7 @@ --> <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>2.0.0-SNAPSHOT</version> @@ -24,7 +24,6 @@ <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> @@ -66,37 +65,45 @@ </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> + <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> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/pom.xml b/pom.xml index 17fb1a712..72f887299 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <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.130</aws.sdk.version> + <aws.sdk.version>2.20.136</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8f3997dbd..cb365cc72 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,10 +31,7 @@ <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> + <cdk.version>2.93.0</cdk.version> </properties> <dependencies> @@ -183,6 +180,14 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> </plugin> + <!-- Don't deploy the e2e tests --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> From 2d3e865f5ee84986d602c523b1f130b043fef35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 1 Sep 2023 08:58:14 +0200 Subject: [PATCH 0462/1008] chore: remove aspectj-rt from the library (#1408) * remove aspectj-rt from the library * set aspectj.version 1.9.20 --- examples/powertools-examples-batch/pom.xml | 65 +---- .../pom.xml | 65 +---- .../cdk/app/pom.xml | 70 +---- .../sam/pom.xml | 195 +++++-------- .../powertools-examples-idempotency/pom.xml | 259 +++++++----------- .../powertools-examples-parameters/pom.xml | 127 +++------ .../powertools-examples-serialization/pom.xml | 72 +---- .../powertools-examples-validation/pom.xml | 130 +++------ powertools-cloudformation/pom.xml | 14 +- powertools-common/pom.xml | 4 - .../handlers/idempotency/pom.xml | 5 +- .../handlers/largemessage/pom.xml | 4 + .../handlers/largemessage_idempotent/pom.xml | 5 +- powertools-e2e-tests/handlers/logging/pom.xml | 5 +- powertools-e2e-tests/handlers/metrics/pom.xml | 5 +- .../handlers/parameters/pom.xml | 5 +- powertools-e2e-tests/handlers/pom.xml | 111 +++----- powertools-e2e-tests/handlers/tracing/pom.xml | 5 +- powertools-idempotency/pom.xml | 9 +- powertools-large-messages/pom.xml | 9 +- powertools-logging/pom.xml | 4 - powertools-metrics/pom.xml | 5 - powertools-parameters/pom.xml | 5 - powertools-tracing/pom.xml | 4 - powertools-validation/pom.xml | 4 - 25 files changed, 395 insertions(+), 791 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index cfc035238..8d2c6052a 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -15,6 +15,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <sdk.version>2.20.133</sdk.version> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -63,6 +64,11 @@ <artifactId>kinesis</artifactId> <version>${sdk.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> </dependencies> <build> @@ -93,6 +99,13 @@ </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> @@ -139,6 +152,7 @@ - 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> @@ -147,57 +161,6 @@ <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-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 85e330742..c8eff3211 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -7,7 +7,7 @@ <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> - <name>AWS Lambda Powertools for Java library Examples - CloudFormation</name> + <name>Powertools for AWS Lambda (Java) library Examples - CloudFormation</name> <properties> <log4j.version>2.20.0</log4j.version> @@ -16,6 +16,7 @@ <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> <aws.sdk.version>2.20.136</aws.sdk.version> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencyManagement> <dependencies> @@ -60,6 +61,11 @@ <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> @@ -89,9 +95,6 @@ <artifactId>log4j-jcl</artifactId> <version>${log4j.version}</version> </dependency> - - - </dependencies> <build> @@ -118,6 +121,13 @@ </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> @@ -172,53 +182,6 @@ <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> - </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-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 76e02ea48..51c222a07 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -7,12 +7,13 @@ <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> + <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics) with CDK</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> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -51,6 +52,11 @@ <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <dependency> <groupId>junit</groupId> @@ -93,6 +99,13 @@ </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> @@ -147,61 +160,6 @@ <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-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index a85bf8941..55b06cbf7 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -7,12 +7,13 @@ <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics)</name> + <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics) with SAM</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> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -51,6 +52,11 @@ <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <dependency> <groupId>junit</groupId> @@ -61,73 +67,71 @@ </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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> + <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> + <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> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <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.1.0</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 @@ -147,61 +151,6 @@ <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-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index b7b710ac1..c28594023 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -26,6 +26,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -64,6 +65,11 @@ <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <dependency> <groupId>org.mockito</groupId> @@ -91,106 +97,104 @@ </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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> + <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> + <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-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="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.1.0</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 @@ -210,61 +214,6 @@ <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> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 7bf354ac1..b856de65e 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -10,6 +10,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -33,6 +34,11 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.2</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <!-- Test dependencies --> <dependency> @@ -56,45 +62,44 @@ </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> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> + <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> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</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 @@ -114,50 +119,6 @@ <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/pom.xml b/examples/powertools-examples-serialization/pom.xml index 13f01a8d3..0dd626ea0 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -71,71 +71,15 @@ <skip>true</skip> </configuration> </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.13.1</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> - <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-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 296504a32..08d2eba32 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -24,6 +24,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -42,6 +43,11 @@ <artifactId>aws-lambda-java-core</artifactId> <version>1.2.3</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <!-- Test dependencies --> <dependency> @@ -65,45 +71,44 @@ </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> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> + <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> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</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 @@ -123,53 +128,6 @@ <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-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> </project> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 336a3f94c..29987fdeb 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -27,7 +27,7 @@ <version>2.0.0-SNAPSHOT</version> </parent> - <name>Powertools for AWS Lambda (Java)library Cloudformation</name> + <name>Powertools for AWS Lambda (Java) library 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. @@ -76,10 +76,6 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <!-- Test dependencies --> <dependency> @@ -116,6 +112,14 @@ <build> <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-checkstyle-plugin</artifactId> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 721f264e0..d85b06486 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -58,10 +58,6 @@ <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> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 25dfbfabf..22b6a1c53 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -29,7 +29,10 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index c626f5f64..8302624ef 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -33,6 +33,10 @@ <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 9635efd87..1fe9092ef 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -33,7 +33,10 @@ <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 4b613f2bf..0ee014116 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -21,7 +21,10 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 35db53899..2f3cabd16 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -21,7 +21,10 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 410cdfb5e..4d5330da0 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -25,7 +25,10 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7abaec9d0..e2f6abb28 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -23,6 +23,7 @@ <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> + <aspectj.version>1.9.20</aspectj.version> </properties> <modules> @@ -98,6 +99,11 @@ <artifactId>log4j-slf4j2-impl</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> </dependencies> </dependencyManagement> @@ -143,6 +149,34 @@ <useIncrementalCompilation>false</useIncrementalCompilation> </configuration> </plugin> + <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> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> + </plugin> </plugins> </pluginManagement> </build> @@ -156,83 +190,6 @@ <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> - <activation> - <jdk>[11,)</jdk> <!-- >= 11 --> - </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> </profile> </profiles> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 252009aa9..b9240c356 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -21,7 +21,10 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 6a39ae6e0..d636d2e75 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -86,10 +86,6 @@ <artifactId>url-connection-client</artifactId> <version>${aws.sdk.version}</version> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <!-- Test dependencies --> <dependency> @@ -122,6 +118,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> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 6cffecb73..2c58fda4c 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -58,10 +58,6 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> @@ -133,6 +129,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> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index e16dd0a8b..c9e6c98df 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -83,10 +83,6 @@ <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <!-- Test dependencies --> <dependency> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index e67aca7e2..feecd4bf2 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -83,11 +83,6 @@ <artifactId>jackson-databind</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> - <!-- Test dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index a703bf36b..18d15ab02 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -104,11 +104,6 @@ <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> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 0c9ed8f71..f55c4f346 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -68,10 +68,6 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-core</artifactId> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 5868629de..cbb6159a6 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -80,10 +80,6 @@ <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> From 8586bf6a3d4bbce426e530d1e088e29cdc69dbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:00:51 +0200 Subject: [PATCH 0463/1008] fix: add aspectj-rt to batch e2e (#1410) * add aspectj-rt * run e2e on v2 updates --- .github/workflows/run-e2e-tests.yml | 1 + powertools-e2e-tests/handlers/batch/pom.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index c4a8c6fb2..5c281a037 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -23,6 +23,7 @@ on: pull_request: branches: - main + - v2 paths: - 'powertools-e2e-tests/**' diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 995121e2a..aee9bf3dd 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -37,6 +37,10 @@ <groupId>software.amazon.awssdk</groupId> <artifactId>dynamodb</artifactId> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> </dependencies> <build> From 2d7f02449ca2ed863cb4446f14f376ef894c1b1b Mon Sep 17 00:00:00 2001 From: walmsles <2704782+walmsles@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:28:47 +1000 Subject: [PATCH 0464/1008] docs(logging): align example cloudwatch example to correct output from code: lambda_request_id --> function_request_id (#1411) --- docs/core/logging.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index 09714a512..2df9a4529 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -347,7 +347,7 @@ You can set a Correlation ID using `correlationIdPath` attribute by passing a [J "functionName": "test", "functionMemorySize": 128, "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", "correlation_id": "correlation_id_value" } ``` @@ -397,7 +397,7 @@ for known event sources, where either a request ID or X-Ray Trace ID are present "functionName": "test", "functionMemorySize": 128, "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", "correlation_id": "correlation_id_value" } ``` @@ -510,7 +510,7 @@ this means that custom keys can be persisted across invocations. If you want all "functionName": "test", "functionMemorySize": 128, "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72", "specialKey": "value" } ``` @@ -527,7 +527,7 @@ this means that custom keys can be persisted across invocations. If you want all "functionName": "test", "functionMemorySize": 128, "functionArn": "arn:aws:lambda:eu-west-1:12345678910:function:test", - "lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72" + "function_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72" } ``` From ec48401c0cd230560a55d98702580e8c9c1eb21b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 06:29:33 +0200 Subject: [PATCH 0465/1008] build(deps): bump aws.sdk.version from 2.20.136 to 2.20.137 (#1406) Bumps `aws.sdk.version` from 2.20.136 to 2.20.137. Updates `software.amazon.awssdk:bom` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:http-client-spi` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:url-connection-client` from 2.20.133 to 2.20.137 Updates `software.amazon.awssdk:sqs` from 2.20.133 to 2.20.137 Updates `software.amazon.awssdk:s3` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:dynamodb` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:lambda` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:kinesis` from 2.20.133 to 2.20.137 Updates `software.amazon.awssdk:cloudwatch` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:xray` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:cloudformation` from 2.20.136 to 2.20.137 Updates `software.amazon.awssdk:sts` from 2.20.136 to 2.20.137 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 1273d8d15..4e9201aef 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.136</aws.sdk.version> + <aws.sdk.version>2.20.137</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index b2ef7145c..7e21bfd28 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.136</version> + <version>2.20.137</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 9066d5880..26e9f1612 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.136</aws.sdk.version> + <aws.sdk.version>2.20.137</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 910b4f2e1cffc53321294524c1fc2a45cc485950 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 06:31:32 +0200 Subject: [PATCH 0466/1008] build(deps): bump com.puppycrawl.tools:checkstyle (#1399) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.12.2 to 10.12.3. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.2...checkstyle-10.12.3) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26e9f1612..b8a02d2ed 100644 --- a/pom.xml +++ b/pom.xml @@ -561,7 +561,7 @@ <dependency> <groupId>com.puppycrawl.tools</groupId> <artifactId>checkstyle</artifactId> - <version>10.12.2</version> + <version>10.12.3</version> </dependency> </dependencies> <executions> From 578fd804754d6585db8faecbb3103f60307cacca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:47:24 +0200 Subject: [PATCH 0467/1008] build(deps): bump aws.sdk.version from 2.20.137 to 2.20.140 (#1412) Bumps `aws.sdk.version` from 2.20.137 to 2.20.140. Updates `software.amazon.awssdk:bom` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:http-client-spi` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:url-connection-client` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:sqs` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:s3` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:dynamodb` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:lambda` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:kinesis` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:cloudwatch` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:xray` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:cloudformation` from 2.20.137 to 2.20.140 Updates `software.amazon.awssdk:sts` from 2.20.137 to 2.20.140 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 4e9201aef..142b2e1e9 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <aws.sdk.version>2.20.137</aws.sdk.version> + <aws.sdk.version>2.20.140</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 7e21bfd28..090905886 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.137</version> + <version>2.20.140</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index b8a02d2ed..01f63364b 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.137</aws.sdk.version> + <aws.sdk.version>2.20.140</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 6debe05a56717cb8ff9f7f353dfec69f36ed08e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:58:08 +0200 Subject: [PATCH 0468/1008] build(deps): bump com.amazonaws:aws-lambda-java-events (#1413) Bumps [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs) from 3.11.2 to 3.11.3. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-events dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/app/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 142b2e1e9..c63bc9dbe 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.11.2</lambda.events.version> + <lambda.events.version>3.11.3</lambda.events.version> <aws.sdk.version>2.20.140</aws.sdk.version> </properties> <dependencyManagement> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index 7831df839..57397f187 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 09cab6cd6..93c858556 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 1afce1f29..a98645a29 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -52,7 +52,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index fe74f859d..deb9c6ba6 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <!-- Test dependencies --> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 7b63d266d..eee3e456a 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <!-- Test dependencies --> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 090905886..65c2e41f3 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -37,7 +37,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/pom.xml b/pom.xml index 01f63364b..d66e97397 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.11.2</lambda.events.version> + <lambda.events.version>3.11.3</lambda.events.version> <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> From 2e769528a662383ece323ea1ef1e471d1f19e362 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 13:36:41 +0200 Subject: [PATCH 0469/1008] build(deps): bump aws.sdk.version from 2.20.133 to 2.20.140 (#1415) Bumps `aws.sdk.version` from 2.20.133 to 2.20.140. Updates `software.amazon.awssdk:url-connection-client` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:sqs` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:sdk-core` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:kinesis` from 2.20.133 to 2.20.140 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.133 to 2.20.140 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 290fe0cd2..7c0db3174 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.133</sdk.version> + <sdk.version>2.20.140</sdk.version> </properties> <dependencies> From 9df5830ffdf331e32b743339bd384e2ed7a9c71b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 13:45:20 +0200 Subject: [PATCH 0470/1008] build(deps): bump aws.sdk.version from 2.20.140 to 2.20.141 (#1421) Bumps `aws.sdk.version` from 2.20.140 to 2.20.141. Updates `software.amazon.awssdk:bom` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:http-client-spi` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:url-connection-client` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:sqs` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:s3` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:dynamodb` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:lambda` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:kinesis` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:cloudwatch` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:xray` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:cloudformation` from 2.20.140 to 2.20.141 Updates `software.amazon.awssdk:sts` from 2.20.140 to 2.20.141 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c63bc9dbe..336f79b65 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.140</aws.sdk.version> + <aws.sdk.version>2.20.141</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 65c2e41f3..e44f715a7 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.140</version> + <version>2.20.141</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index d66e97397..009730a4b 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.140</aws.sdk.version> + <aws.sdk.version>2.20.141</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 5d8e8ba78ea5f2e9620604e3ee719d982a27883a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 13:36:04 +0200 Subject: [PATCH 0471/1008] build(deps): bump aws.sdk.version from 2.20.140 to 2.20.142 (#1423) Bumps `aws.sdk.version` from 2.20.140 to 2.20.142. Updates `software.amazon.awssdk:url-connection-client` from 2.20.140 to 2.20.142 Updates `software.amazon.awssdk:sqs` from 2.20.140 to 2.20.142 Updates `software.amazon.awssdk:sdk-core` from 2.20.140 to 2.20.142 Updates `software.amazon.awssdk:kinesis` from 2.20.140 to 2.20.142 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.140 to 2.20.142 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 7c0db3174..1e87e9754 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.140</sdk.version> + <sdk.version>2.20.142</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index e44f715a7..1dcb9ca6e 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.141</version> + <version>2.20.142</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 8935593cf162c3eed93d40098bf5a81fc1b38a21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 14:08:43 +0200 Subject: [PATCH 0472/1008] build(deps): bump aws.sdk.version from 2.20.141 to 2.20.143 (#1424) Bumps `aws.sdk.version` from 2.20.141 to 2.20.143. Updates `software.amazon.awssdk:url-connection-client` from 2.20.141 to 2.20.143 Updates `software.amazon.awssdk:sqs` from 2.20.141 to 2.20.143 Updates `software.amazon.awssdk:sdk-core` from 2.20.142 to 2.20.143 Updates `software.amazon.awssdk:kinesis` from 2.20.141 to 2.20.143 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.142 to 2.20.143 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 1e87e9754..7ab895c07 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.142</sdk.version> + <sdk.version>2.20.143</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 1dcb9ca6e..12a07a1ce 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.142</version> + <version>2.20.143</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From f66b46bc953ca2ba46d4d7ddc2fb232d5a2045e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 14:35:16 +0200 Subject: [PATCH 0473/1008] build(deps): bump com.github.tomakehurst:wiremock-jre8 (#1426) Bumps [com.github.tomakehurst:wiremock-jre8](https://github.com/wiremock/wiremock) from 2.35.0 to 2.35.1. - [Release notes](https://github.com/wiremock/wiremock/releases) - [Commits](https://github.com/wiremock/wiremock/compare/2.35.0...2.35.1) --- updated-dependencies: - dependency-name: com.github.tomakehurst:wiremock-jre8 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 009730a4b..0bb1de3c6 100644 --- a/pom.xml +++ b/pom.xml @@ -307,7 +307,7 @@ <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> - <version>2.35.0</version> + <version>2.35.1</version> <scope>test</scope> </dependency> </dependencies> From 6b5b1a4a497e1b9621503f3c539b877508cdf48e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:16:01 +0200 Subject: [PATCH 0474/1008] build(deps): bump aws.sdk.version from 2.20.141 to 2.20.144 (#1427) Bumps `aws.sdk.version` from 2.20.141 to 2.20.144. Updates `software.amazon.awssdk:bom` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:http-client-spi` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:url-connection-client` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:sqs` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:s3` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:dynamodb` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:lambda` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:kinesis` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:cloudwatch` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:xray` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:cloudformation` from 2.20.141 to 2.20.144 Updates `software.amazon.awssdk:sts` from 2.20.141 to 2.20.144 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 336f79b65..7d49819e4 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.141</aws.sdk.version> + <aws.sdk.version>2.20.144</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 12a07a1ce..2ec7a0071 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.143</version> + <version>2.20.144</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 0bb1de3c6..e688c5890 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.141</aws.sdk.version> + <aws.sdk.version>2.20.144</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 18294afd40e7200df90e8e256e18d050f1e26957 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:48:26 +0200 Subject: [PATCH 0475/1008] docs: Fix link to SQS large message migration guide (#1422) * Fix link to SQS large message migration guide * Better --- docs/utilities/sqs_large_message_handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utilities/sqs_large_message_handling.md b/docs/utilities/sqs_large_message_handling.md index 0924d01cf..9d721f544 100644 --- a/docs/utilities/sqs_large_message_handling.md +++ b/docs/utilities/sqs_large_message_handling.md @@ -6,7 +6,7 @@ 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) + [the migration guide](large_messages.md#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 From 3dfebee220549d4d19a7500bcd483ae01a8f0dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 11 Sep 2023 17:46:29 +0200 Subject: [PATCH 0476/1008] fix #1419 (#1420) --- .../persistence/BasePersistenceStore.java | 37 +++++++++---------- .../internal/IdempotencyAspectTest.java | 22 +++++++++++ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index f58b276fd..a3b1b0ff4 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -26,7 +26,6 @@ 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; @@ -122,7 +121,7 @@ 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), @@ -130,9 +129,9 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { 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); @@ -164,7 +163,7 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime OptionalLong.of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); } - DataRecord record = new DataRecord( + DataRecord dataRecord = new DataRecord( idempotencyKey, DataRecord.Status.INPROGRESS, getExpiryEpochSecond(now), @@ -172,8 +171,8 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime getHashedPayload(data), inProgressExpirationMsTimestamp ); - LOG.debug("saving in progress record for idempotency key: {}", record.getIdempotencyKey()); - putRecord(record, now); + LOG.debug("saving in progress record for idempotency key: {}", dataRecord.getIdempotencyKey()); + putRecord(dataRecord, now); } /** @@ -223,10 +222,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 +257,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), + Stream<JsonNode> stream = + StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.elements(), Spliterator.ORDERED), false); - return stream.allMatch(e -> e.getValue().isNull()); + return stream.allMatch(JsonNode::isNull); } return data.isNull(); } @@ -378,10 +377,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); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java index 9a30472a8..c72593b66 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -378,6 +378,28 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey( "testFunction.createBasket#a1d0c6e83f027327d8461063f4ac58a6"); } + @Test + public 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 public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { Idempotency.config() From aa56890302ea9270f0fd559b7804637fba749ac0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 13:37:54 +0200 Subject: [PATCH 0477/1008] build(deps): bump aws.sdk.version from 2.20.143 to 2.20.145 (#1429) Bumps `aws.sdk.version` from 2.20.143 to 2.20.145. Updates `software.amazon.awssdk:url-connection-client` from 2.20.143 to 2.20.145 Updates `software.amazon.awssdk:sqs` from 2.20.143 to 2.20.145 Updates `software.amazon.awssdk:sdk-core` from 2.20.143 to 2.20.145 Updates `software.amazon.awssdk:kinesis` from 2.20.143 to 2.20.145 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.143 to 2.20.145 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 7ab895c07..1dbb5b789 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.143</sdk.version> + <sdk.version>2.20.145</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 2ec7a0071..caceab211 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.144</version> + <version>2.20.145</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 270b78fcdba4552b6efb0f8f3b6f3d8f73b41954 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:30:44 +0200 Subject: [PATCH 0478/1008] build(deps): bump aws.sdk.version from 2.20.144 to 2.20.146 (#1430) Bumps `aws.sdk.version` from 2.20.144 to 2.20.146. Updates `software.amazon.awssdk:bom` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:http-client-spi` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:url-connection-client` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:sqs` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:s3` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:dynamodb` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:lambda` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:kinesis` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:cloudwatch` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:xray` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:cloudformation` from 2.20.144 to 2.20.146 Updates `software.amazon.awssdk:sts` from 2.20.144 to 2.20.146 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 7d49819e4..5c01f3ce9 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.144</aws.sdk.version> + <aws.sdk.version>2.20.146</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index caceab211..5ae2f8b80 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.145</version> + <version>2.20.146</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index e688c5890..5bfa33373 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.144</aws.sdk.version> + <aws.sdk.version>2.20.146</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From ff4571f359a870b2040befde9fa0df10cd287670 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:35:35 +0200 Subject: [PATCH 0479/1008] build(deps): bump aws.sdk.version from 2.20.146 to 2.20.147 (#1431) Bumps `aws.sdk.version` from 2.20.146 to 2.20.147. Updates `software.amazon.awssdk:bom` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:http-client-spi` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:url-connection-client` from 2.20.145 to 2.20.147 Updates `software.amazon.awssdk:sqs` from 2.20.145 to 2.20.147 Updates `software.amazon.awssdk:s3` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:dynamodb` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:lambda` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:kinesis` from 2.20.145 to 2.20.147 Updates `software.amazon.awssdk:cloudwatch` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:xray` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:cloudformation` from 2.20.146 to 2.20.147 Updates `software.amazon.awssdk:sts` from 2.20.146 to 2.20.147 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 5c01f3ce9..dbb53f497 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.146</aws.sdk.version> + <aws.sdk.version>2.20.147</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 5ae2f8b80..75dd2472c 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.146</version> + <version>2.20.147</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 5bfa33373..ba83e3d34 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.146</aws.sdk.version> + <aws.sdk.version>2.20.147</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 23b779d65a4044d9b369a7ecbf4e3e704c343f71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Sep 2023 13:45:05 +0200 Subject: [PATCH 0480/1008] build(deps): bump aws.sdk.version from 2.20.147 to 2.20.148 (#1433) Bumps `aws.sdk.version` from 2.20.147 to 2.20.148. Updates `software.amazon.awssdk:bom` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:http-client-spi` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:url-connection-client` from 2.20.145 to 2.20.148 Updates `software.amazon.awssdk:sqs` from 2.20.145 to 2.20.148 Updates `software.amazon.awssdk:s3` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:dynamodb` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:lambda` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:kinesis` from 2.20.145 to 2.20.148 Updates `software.amazon.awssdk:cloudwatch` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:xray` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:cloudformation` from 2.20.147 to 2.20.148 Updates `software.amazon.awssdk:sts` from 2.20.147 to 2.20.148 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index dbb53f497..bdc55ab17 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.147</aws.sdk.version> + <aws.sdk.version>2.20.148</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 75dd2472c..1041242ff 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.147</version> + <version>2.20.148</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index ba83e3d34..ae79a72a1 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.147</aws.sdk.version> + <aws.sdk.version>2.20.148</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 4b0b5a862ce5921413a9440997f61ea4e1b297d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 13:47:39 +0200 Subject: [PATCH 0481/1008] build(deps): bump aws.sdk.version from 2.20.145 to 2.20.149 (#1436) Bumps `aws.sdk.version` from 2.20.145 to 2.20.149. Updates `software.amazon.awssdk:url-connection-client` from 2.20.145 to 2.20.149 Updates `software.amazon.awssdk:sqs` from 2.20.145 to 2.20.149 Updates `software.amazon.awssdk:sdk-core` from 2.20.145 to 2.20.149 Updates `software.amazon.awssdk:kinesis` from 2.20.145 to 2.20.149 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.145 to 2.20.149 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 1dbb5b789..031b9c6d8 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.145</sdk.version> + <sdk.version>2.20.149</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 1041242ff..a59f6193b 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.148</version> + <version>2.20.149</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 61db38619d2178ec01175e357bbdf0a1cbbcc168 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:04:11 +0200 Subject: [PATCH 0482/1008] build(deps): bump aws.sdk.version from 2.20.148 to 2.20.150 (#1437) Bumps `aws.sdk.version` from 2.20.148 to 2.20.150. Updates `software.amazon.awssdk:bom` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:http-client-spi` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:url-connection-client` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:sqs` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:s3` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:dynamodb` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:lambda` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:kinesis` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:cloudwatch` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:xray` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:cloudformation` from 2.20.148 to 2.20.150 Updates `software.amazon.awssdk:sts` from 2.20.148 to 2.20.150 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index bdc55ab17..37b4c56a1 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.148</aws.sdk.version> + <aws.sdk.version>2.20.150</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index a59f6193b..0d932a53c 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.149</version> + <version>2.20.150</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index ae79a72a1..e36616b3a 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.148</aws.sdk.version> + <aws.sdk.version>2.20.150</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 49f2469667592227c0cbf5e9d4efaefbafa43981 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:37:38 +0200 Subject: [PATCH 0483/1008] build(deps): bump aws.sdk.version from 2.20.150 to 2.20.151 (#1438) Bumps `aws.sdk.version` from 2.20.150 to 2.20.151. Updates `software.amazon.awssdk:bom` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:http-client-spi` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:url-connection-client` from 2.20.149 to 2.20.151 Updates `software.amazon.awssdk:sqs` from 2.20.149 to 2.20.151 Updates `software.amazon.awssdk:s3` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:dynamodb` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:lambda` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:kinesis` from 2.20.149 to 2.20.151 Updates `software.amazon.awssdk:cloudwatch` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:xray` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:cloudformation` from 2.20.150 to 2.20.151 Updates `software.amazon.awssdk:sts` from 2.20.150 to 2.20.151 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 37b4c56a1..6db211489 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.150</aws.sdk.version> + <aws.sdk.version>2.20.151</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0d932a53c..6a2d9e6cf 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.150</version> + <version>2.20.151</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index e36616b3a..809f2af72 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.150</aws.sdk.version> + <aws.sdk.version>2.20.151</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 15d7a99a82e6703deee017727b35947977e51b7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:05:20 +0200 Subject: [PATCH 0484/1008] build(deps): bump aws.sdk.version from 2.20.149 to 2.20.152 (#1439) Bumps `aws.sdk.version` from 2.20.149 to 2.20.152. Updates `software.amazon.awssdk:url-connection-client` from 2.20.149 to 2.20.152 Updates `software.amazon.awssdk:sqs` from 2.20.149 to 2.20.152 Updates `software.amazon.awssdk:sdk-core` from 2.20.149 to 2.20.152 Updates `software.amazon.awssdk:kinesis` from 2.20.149 to 2.20.152 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.149 to 2.20.152 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 031b9c6d8..ea5d1e5f1 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.149</sdk.version> + <sdk.version>2.20.152</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 6a2d9e6cf..d9fa8fff1 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.151</version> + <version>2.20.152</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 07d91184d5ff58169023c330d1c57e96f861014a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 13:44:45 +0200 Subject: [PATCH 0485/1008] build(deps): bump aws.sdk.version from 2.20.151 to 2.20.152 (#1440) Bumps `aws.sdk.version` from 2.20.151 to 2.20.152. Updates `software.amazon.awssdk:bom` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:http-client-spi` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:url-connection-client` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:sqs` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:s3` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:dynamodb` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:lambda` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:kinesis` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:cloudwatch` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:xray` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:cloudformation` from 2.20.151 to 2.20.152 Updates `software.amazon.awssdk:sts` from 2.20.151 to 2.20.152 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 6db211489..6b8e90ebd 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.151</aws.sdk.version> + <aws.sdk.version>2.20.152</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/pom.xml b/pom.xml index 809f2af72..a74e13fc2 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.151</aws.sdk.version> + <aws.sdk.version>2.20.152</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 49ae687b6580c9cc693b234a4b5187c6a17bb9cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 14:03:57 +0200 Subject: [PATCH 0486/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1443) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.93.0 to 2.97.1. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.93.0...v2.97.1) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 6facf8a46..99598ba58 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>1.18.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.93.0</cdk.version> + <cdk.version>2.97.1</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index d3acde3ea..c09d03c61 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <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.93.0</cdk.version> + <cdk.version>2.97.1</cdk.version> </properties> <dependencies> From 6a94862bda9f26209f5762fc439f1076e23b19a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 14:14:30 +0200 Subject: [PATCH 0487/1008] build(deps-dev): bump software.constructs:constructs (#1407) Bumps [software.constructs:constructs](https://github.com/aws/constructs) from 10.2.69 to 10.2.70. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.69...v10.2.70) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index c09d03c61..90427b797 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -30,7 +30,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.69</constructs.version> + <constructs.version>10.2.70</constructs.version> <cdk.version>2.97.1</cdk.version> </properties> From 24393d8b3e27d2cef6654e12653766236bc70e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:13:26 +0200 Subject: [PATCH 0488/1008] chore: Reporting size of the jars in GitHub comments (#1196) * reporting size of the jars in the github comments --- .github/workflows/pr_artifacts_size.yml | 57 +++++++++++++++++++ .github/workflows/{build.yml => pr_build.yml} | 0 pom.xml | 8 +++ 3 files changed, 65 insertions(+) create mode 100644 .github/workflows/pr_artifacts_size.yml rename .github/workflows/{build.yml => pr_build.yml} (100%) diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml new file mode 100644 index 000000000..cbacd78da --- /dev/null +++ b/.github/workflows/pr_artifacts_size.yml @@ -0,0 +1,57 @@ +name: Artifacts Size + +on: + pull_request: + branches: + - master + paths: + - 'powertools-cloudformation/**' + - 'powertools-core/**' + - 'powertools-serialization/**' + - 'powertools-logging/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'powertools-parameters/**' + - 'powertools-idempotency/**' + - 'powertools-metrics/**' + - 'pom.xml' +jobs: + codecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java JDK 11 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: 11 + - name: Build with Maven + run: mvn clean package --file pom.xml -DskipTests + - name: Get artifacts size & build report + id: artifacts-size-report + run: | + echo '## :floppy_disk: Artifacts Size Report' > report.md + echo '| Module | Version | Size (KB) |' >> report.md + echo '| --- | --- | --- |' >> report.md + artifact_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + for artifact in $(cat target/powertools-parent-*.buildinfo | grep 'outputs.*.jar' | grep -v 'sources.jar'); do + artifact_name=$(echo "$artifact" | cut -d '=' -f2) + artifact_name=${artifact_name%-$artifact_version.jar} + artifact_size=$(grep "${artifact%%.filename*}.length" target/powertools-parent-*.buildinfo | cut -d '=' -f2) + printf "| %s | %s | %.2f |\n" "$artifact_name" "$artifact_version" "$(bc <<< "scale=2; $artifact_size/1000")" >> report.md + done + - name: Find potential existing report + uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # 2.4.0 + id: find-comment + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: Artifacts Size Report + - name: Write artifacts size report in comment + uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # 3.0.2 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body-path: 'report.md' + edit-mode: replace \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/pr_build.yml similarity index 100% rename from .github/workflows/build.yml rename to .github/workflows/pr_build.yml diff --git a/pom.xml b/pom.xml index a74e13fc2..36abf162e 100644 --- a/pom.xml +++ b/pom.xml @@ -368,6 +368,14 @@ <useIncrementalCompilation>false</useIncrementalCompilation> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-artifact-plugin</artifactId> + <version>3.4.1</version> + <configuration> + <reproducible>true</reproducible> + </configuration> + </plugin> <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> From c862a8c53cbbf0988e0e6763a21e787837edffc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 13:26:55 +0200 Subject: [PATCH 0489/1008] build(deps): bump aws.sdk.version from 2.20.152 to 2.20.153 (#1441) Bumps `aws.sdk.version` from 2.20.152 to 2.20.153. --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 6b8e90ebd..8fdcb8c08 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.152</aws.sdk.version> + <aws.sdk.version>2.20.153</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index d9fa8fff1..c0c302757 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.152</version> + <version>2.20.153</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 36abf162e..9e23dcef2 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.152</aws.sdk.version> + <aws.sdk.version>2.20.153</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From dd364dd6bbd93523e9669d2fd14eda377d9739a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:15:44 +0200 Subject: [PATCH 0490/1008] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin (#1448) Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.7.3.5 to 4.7.3.6. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.7.3.5...spotbugs-maven-plugin-4.7.3.6) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9e23dcef2..4f4ccb2bd 100644 --- a/pom.xml +++ b/pom.xml @@ -505,7 +505,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.5</version> + <version>4.7.3.6</version> <executions> <execution> <id>test</id> From 4d3484fdea1a519f06569ef688efd2bf32a689b8 Mon Sep 17 00:00:00 2001 From: Alexey Soshin <alexey.soshin@gmail.com> Date: Thu, 28 Sep 2023 08:12:20 +0100 Subject: [PATCH 0491/1008] docs: Add Serveless Framework example (#1363) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial * Add memory size and timeouts, same as SAM examples * Add useful Serverless commands * Remove redundant function * Update examples/powertools-examples-core/serverless/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/serverless/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/serverless/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/serverless/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update examples/powertools-examples-core/serverless/README.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Remove reduntant command from README * Move most of Powertools configuration to the service-wide environment * Remove more generated comments * Add newlines * Comment out annotations that are preceded by environment variables * Add link to the Serverless example * Update examples/powertools-examples-core/serverless/pom.xml Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update examples/powertools-examples-core/serverless/pom.xml Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update examples/powertools-examples-core/serverless/pom.xml Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Remove POWERTOOLS_LOGGER_LOG_EVENT, since it's unsupported --------- Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- examples/README.md | 1 + examples/pom.xml | 1 + examples/powertools-examples-core/README.md | 1 + .../serverless/README.md | 26 +++ .../serverless/pom.xml | 209 ++++++++++++++++++ .../serverless/serverless.yml | 40 ++++ .../src/main/java/helloworld/App.java | 105 +++++++++ .../src/main/java/helloworld/AppStream.java | 38 ++++ .../serverless/src/main/resources/log4j2.xml | 16 ++ .../src/test/java/helloworld/AppTest.java | 59 +++++ 10 files changed, 496 insertions(+) create mode 100644 examples/powertools-examples-core/serverless/README.md create mode 100644 examples/powertools-examples-core/serverless/pom.xml create mode 100644 examples/powertools-examples-core/serverless/serverless.yml create mode 100644 examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java diff --git a/examples/README.md b/examples/README.md index 0744c2bb1..52dc0c1e9 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,7 @@ Each example can be copied from its subdirectory and used independently of the r * [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) + * [Serverless](./powertools-examples-core/serverless) * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads diff --git a/examples/pom.xml b/examples/pom.xml index eae9e10e5..810ec1b36 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -32,6 +32,7 @@ <module>powertools-examples-core/sam</module> <module>powertools-examples-core/cdk/app</module> <module>powertools-examples-core/cdk/infra</module> + <module>powertools-examples-core/serverless</module> <module>powertools-examples-idempotency</module> <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index d690b01c5..1d1dd031f 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -9,6 +9,7 @@ We provide examples for the following infrastructure-as-code tools: * [AWS SAM](sam/) * [AWS CDK](cdk/) +* [Serverless framework](serverless/) We also provide an example showing the integration of SAM, Powertools, and Gradle: diff --git a/examples/powertools-examples-core/serverless/README.md b/examples/powertools-examples-core/serverless/README.md new file mode 100644 index 000000000..aec093182 --- /dev/null +++ b/examples/powertools-examples-core/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/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml new file mode 100644 index 000000000..793318da3 --- /dev/null +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -0,0 +1,209 @@ +<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.18.0-SNAPSHOT</version> + <artifactId>powertools-examples-core-serverless</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> + </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> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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> + <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/serverless/serverless.yml b/examples/powertools-examples-core/serverless/serverless.yml new file mode 100644 index 000000000..d8dec8080 --- /dev/null +++ b/examples/powertools-examples-core/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/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java new file mode 100644 index 000000000..dacd7f1d4 --- /dev/null +++ b/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + + // This is controlled by POWERTOOLS_LOGGER_SAMPLE_RATE environment variable + // @Logging(logEvent = true, samplingRate = 0.7) + // This is controlled by POWERTOOLS_METRICS_NAMESPACE environment variable + // @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // This is controlled by POWERTOOLS_TRACER_CAPTURE_ERROR environment variable + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + 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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml new file mode 100644 index 000000000..0cc0953f0 --- /dev/null +++ b/examples/powertools-examples-core/serverless/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/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..70dad8d71 --- /dev/null +++ b/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.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 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\"")); + } +} From fa75c7499ab2a8566ad90e6f10dfe84bb873dd1d Mon Sep 17 00:00:00 2001 From: Alexander Schueren <am29d@hey.com> Date: Mon, 2 Oct 2023 11:48:09 +0200 Subject: [PATCH 0492/1008] docs: apply line highlight only for default light mode (#1453) --- docs/stylesheets/extra.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index de2f3b5f9..d135d7210 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -3,7 +3,9 @@ } .highlight .hll { - background-color: lavender + [data-md-color-scheme="default"] { + background-color: lavender; + } } .md-typeset table:not([class]) { From 1140fcfb2ceb16a701608ec82dc255142ac6c815 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:54:53 +0200 Subject: [PATCH 0493/1008] fix: Fix schema validation unit test build issues (#1456) * Is it just the one test? * What about this * Temporary fix --- .../validation/ValidationUtilsTest.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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..cd6ad160c 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 @@ -70,12 +70,19 @@ public void testLoadMetaSchema_NoValidation() { }); } - @Test - public void testLoadMetaSchemaV2019() { - ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V201909); - JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V201909", true); - assertThat(jsonSchema).isNotNull(); - } + /** + * TODO - get this going again on github; commented out to unblock build, and seems to run outside of github + * workers still. See here --> + * + * https://github.com/aws-powertools/powertools-lambda-java/actions/runs/6417845031/job/17424409856?pr=1456 + * https://github.com/aws-powertools/powertools-lambda-java/issues/1455 + */ +// @Test +// public void testLoadMetaSchemaV2019() { +// ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V201909); +// JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V201909", true); +// assertThat(jsonSchema).isNotNull(); +// } @Test public void testLoadMetaSchemaV7() { From 3ea3fa101a5a6a6228051abc70958c10b5374c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:59:29 +0200 Subject: [PATCH 0494/1008] Use URITranslator to use local files instead of remote (#1457) Remove local schema files, use the ones provided in the dependency Add support for 202012 Update tests --- .../validation/ValidationConfig.java | 2 +- .../validation/ValidationUtils.java | 13 +- .../main/resources/schemas/meta/applicator | 56 ------ .../src/main/resources/schemas/meta/content | 17 -- .../src/main/resources/schemas/meta/core | 57 ------ .../src/main/resources/schemas/meta/format | 14 -- .../src/main/resources/schemas/meta/meta-data | 37 ---- .../main/resources/schemas/meta/validation | 98 ---------- .../resources/schemas/meta_schema_V201909 | 42 ----- .../src/main/resources/schemas/meta_schema_V4 | 149 --------------- .../src/main/resources/schemas/meta_schema_V6 | 155 ---------------- .../src/main/resources/schemas/meta_schema_V7 | 172 ------------------ .../validation/ValidationUtilsTest.java | 34 ++-- 13 files changed, 27 insertions(+), 819 deletions(-) delete mode 100644 powertools-validation/src/main/resources/schemas/meta/applicator delete mode 100644 powertools-validation/src/main/resources/schemas/meta/content delete mode 100644 powertools-validation/src/main/resources/schemas/meta/core delete mode 100644 powertools-validation/src/main/resources/schemas/meta/format delete mode 100644 powertools-validation/src/main/resources/schemas/meta/meta-data delete mode 100644 powertools-validation/src/main/resources/schemas/meta/validation delete mode 100644 powertools-validation/src/main/resources/schemas/meta_schema_V201909 delete mode 100644 powertools-validation/src/main/resources/schemas/meta_schema_V4 delete mode 100644 powertools-validation/src/main/resources/schemas/meta_schema_V6 delete mode 100644 powertools-validation/src/main/resources/schemas/meta_schema_V7 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..143f0584d 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 @@ -49,7 +49,7 @@ public SpecVersion.VersionFlag getSchemaVersion() { /** * Set the version of the json schema specifications (default is V7) * - * @param version May be V4, V6, V7 or V201909 + * @param version May be V4, V6, V7, V201909 or V202012 */ public void setSchemaVersion(SpecVersion.VersionFlag version) { if (version != jsonSchemaVersion) { 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..221e5fb1d 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,7 +21,9 @@ import com.fasterxml.jackson.databind.node.JsonNodeType; import com.fasterxml.jackson.databind.node.NullNode; import com.networknt.schema.JsonSchema; +import com.networknt.schema.SchemaValidatorsConfig; import com.networknt.schema.ValidationMessage; +import com.networknt.schema.uri.URITranslator; import io.burt.jmespath.Expression; import java.io.ByteArrayOutputStream; import java.io.InputStream; @@ -257,7 +259,10 @@ private static JsonSchema createJsonSchema(String schema) { String filePath = schema.substring(CLASSPATH.length()); try (InputStream schemaStream = ValidationAspect.class.getResourceAsStream(filePath)) { - jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream); + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + config.addUriTranslator(URITranslator.prefix("https://json-schema.org", "resource:")); + + jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream, config); } catch (Exception e) { throw new IllegalArgumentException( "'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); @@ -270,13 +275,13 @@ private static JsonSchema createJsonSchema(String schema) { } private static void validateSchema(String schema, JsonSchema jsonSchema) { - String version = ValidationConfig.get().getSchemaVersion().toString(); + String schemaId = ValidationConfig.get().getSchemaVersion().getId().replace("https://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/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/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java index cd6ad160c..d80670669 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 @@ -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 @@ -70,38 +70,38 @@ public void testLoadMetaSchema_NoValidation() { }); } - /** - * TODO - get this going again on github; commented out to unblock build, and seems to run outside of github - * workers still. See here --> - * - * https://github.com/aws-powertools/powertools-lambda-java/actions/runs/6417845031/job/17424409856?pr=1456 - * https://github.com/aws-powertools/powertools-lambda-java/issues/1455 - */ -// @Test -// public void testLoadMetaSchemaV2019() { -// ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V201909); -// JsonSchema jsonSchema = getJsonSchema("classpath:/schemas/meta_schema_V201909", true); -// assertThat(jsonSchema).isNotNull(); -// } + @Test + public void testLoadMetaSchemaV2019() { + ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V201909); + 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(); } From 965c897d00ef2995c1efb8413115c4a1e18ae098 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:42:53 +0200 Subject: [PATCH 0495/1008] build(deps): bump aws.sdk.version from 2.20.153 to 2.20.155 (#1447) Bumps `aws.sdk.version` from 2.20.153 to 2.20.155. Updates `software.amazon.awssdk:bom` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:http-client-spi` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:url-connection-client` from 2.20.152 to 2.20.155 Updates `software.amazon.awssdk:sqs` from 2.20.152 to 2.20.155 Updates `software.amazon.awssdk:s3` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:dynamodb` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:lambda` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:kinesis` from 2.20.152 to 2.20.155 Updates `software.amazon.awssdk:cloudwatch` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:xray` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:cloudformation` from 2.20.153 to 2.20.155 Updates `software.amazon.awssdk:sts` from 2.20.153 to 2.20.155 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8fdcb8c08..db66b8675 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.153</aws.sdk.version> + <aws.sdk.version>2.20.155</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index c0c302757..38b46c020 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.153</version> + <version>2.20.155</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 4f4ccb2bd..ee3bc1c2e 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.153</aws.sdk.version> + <aws.sdk.version>2.20.155</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From e99bfad9f93f3bbac7e4e32670be6d46017cdf1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:43:59 +0200 Subject: [PATCH 0496/1008] build(deps): bump com.networknt:json-schema-validator (#1446) Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.86 to 1.0.87. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.86...1.0.87) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 23841777e..606f02139 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -87,7 +87,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.86</version> + <version>1.0.87</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 349ea0b937a1b39b7b8926ae6985f4545c9fe33f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:58:49 +0200 Subject: [PATCH 0497/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1461) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.97.1 to 2.100.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.97.1...v2.100.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 99598ba58..216bf35d4 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>1.18.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.97.1</cdk.version> + <cdk.version>2.100.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 90427b797..89a255c8b 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.70</constructs.version> - <cdk.version>2.97.1</cdk.version> + <cdk.version>2.100.0</cdk.version> </properties> <dependencies> From aa53bd924573faf2ae3687d3b07c459a6640b419 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 13:19:10 +0200 Subject: [PATCH 0498/1008] build(deps): bump aws.sdk.version from 2.20.155 to 2.20.162 (#1462) Bumps `aws.sdk.version` from 2.20.155 to 2.20.162. --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index db66b8675..c696ef0a8 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.155</aws.sdk.version> + <aws.sdk.version>2.20.162</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 38b46c020..988291413 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.155</version> + <version>2.20.162</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index ee3bc1c2e..277f2f681 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.155</aws.sdk.version> + <aws.sdk.version>2.20.162</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 897f7e9af665299695f5613f3607348e613f46a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:02:49 +0200 Subject: [PATCH 0499/1008] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin (#1473) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.5.0...maven-javadoc-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 277f2f681..fd31bef37 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.10</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-javadoc-plugin.version>3.6.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> From c5ced4f70e8fcec8d635aadbc136a8cac7ba24d6 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Fri, 13 Oct 2023 13:55:58 +0200 Subject: [PATCH 0500/1008] Remove powertools-examples-core --- .../powertools-examples-core/cdk/README.md | 36 --- .../cdk/app/events/event.json | 63 ----- .../powertools-examples-core/cdk/app/pom.xml | 241 ----------------- .../cdk/app/src/main/java/helloworld/App.java | 107 -------- .../src/main/java/helloworld/AppStream.java | 38 --- .../cdk/app/src/main/resources/log4j2.xml | 16 -- .../app/src/test/java/helloworld/AppTest.java | 59 ----- .../cdk/infra/cdk.json | 36 --- .../cdk/infra/pom.xml | 63 ----- .../cdk/infra/src/main/java/cdk/CdkApp.java | 53 ---- .../cdk/infra/src/main/java/cdk/CdkStack.java | 144 ---------- .../infra/src/test/java/cdk/CdkStackTest.java | 48 ---- .../powertools-examples-core/gradle/README.md | 38 --- .../gradle/build.gradle | 35 --- .../gradle/events/event.json | 62 ----- .../gradle/gradle/wrapper/.gitignore | 2 - .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - .../powertools-examples-core/gradle/gradlew | 248 ------------------ .../gradle/gradlew.bat | 92 ------- .../gradle/src/main/java/helloworld/App.java | 107 -------- .../src/main/java/helloworld/AppStream.java | 38 --- .../src/test/java/helloworld/AppTest.java | 24 -- .../gradle/template.yaml | 71 ----- .../powertools-examples-core/sam/README.md | 24 -- .../sam/events/event.json | 63 ----- examples/powertools-examples-core/sam/pom.xml | 207 --------------- .../sam/src/main/java/helloworld/App.java | 107 -------- .../src/main/java/helloworld/AppStream.java | 38 --- .../sam/src/main/resources/log4j2.xml | 16 -- .../sam/src/test/java/helloworld/AppTest.java | 59 ----- .../sam/template.yaml | 66 ----- .../serverless/README.md | 26 -- .../serverless/pom.xml | 209 --------------- .../serverless/serverless.yml | 40 --- .../src/main/java/helloworld/App.java | 117 --------- .../src/main/java/helloworld/AppStream.java | 38 --- .../serverless/src/main/resources/log4j2.xml | 16 -- .../src/test/java/helloworld/AppTest.java | 59 ----- 39 files changed, 2713 deletions(-) delete mode 100644 examples/powertools-examples-core/cdk/README.md delete mode 100644 examples/powertools-examples-core/cdk/app/events/event.json delete mode 100644 examples/powertools-examples-core/cdk/app/pom.xml delete mode 100644 examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java delete mode 100644 examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java delete mode 100644 examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml delete mode 100644 examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/cdk/infra/cdk.json delete mode 100644 examples/powertools-examples-core/cdk/infra/pom.xml delete mode 100644 examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java delete mode 100644 examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java delete mode 100644 examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java delete mode 100644 examples/powertools-examples-core/gradle/README.md delete mode 100644 examples/powertools-examples-core/gradle/build.gradle delete mode 100644 examples/powertools-examples-core/gradle/events/event.json delete mode 100644 examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore delete mode 100644 examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar delete mode 100644 examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties delete mode 100755 examples/powertools-examples-core/gradle/gradlew delete mode 100644 examples/powertools-examples-core/gradle/gradlew.bat delete mode 100644 examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java delete mode 100644 examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java delete mode 100644 examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/gradle/template.yaml delete mode 100644 examples/powertools-examples-core/sam/README.md delete mode 100644 examples/powertools-examples-core/sam/events/event.json delete mode 100644 examples/powertools-examples-core/sam/pom.xml delete mode 100644 examples/powertools-examples-core/sam/src/main/java/helloworld/App.java delete mode 100644 examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java delete mode 100644 examples/powertools-examples-core/sam/src/main/resources/log4j2.xml delete mode 100644 examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/sam/template.yaml delete mode 100644 examples/powertools-examples-core/serverless/README.md delete mode 100644 examples/powertools-examples-core/serverless/pom.xml delete mode 100644 examples/powertools-examples-core/serverless/serverless.yml delete mode 100644 examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java delete mode 100644 examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java delete mode 100644 examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml delete mode 100644 examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core/cdk/README.md deleted file mode 100644 index f15a24168..000000000 --- a/examples/powertools-examples-core/cdk/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example with CDK - -This project demonstrates the Lambda for Powertools Java module deployed using [Cloud Development Kit](https://aws.amazon.com/cdk/). - -For general information on the deployed example itself, you can refer to the parent [README](../README.md) - -## 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 - - [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. - -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 - -The minimum to deploy the app should be -```bash -cdk deploy -``` - -If you're running CDK for the first time, you'll need to first run the bootstrap command: -```bash -cdk bootstrap -``` - -## Useful commands - -* `mvn package` compile and run tests -* `cdk synth` emits the synthesized CloudFormation template -* `cdk deploy` deploy this stack to your default AWS account/region -* `cdk diff` compare deployed stack with current state -* `cdk docs` open CDK documentation \ No newline at end of file diff --git a/examples/powertools-examples-core/cdk/app/events/event.json b/examples/powertools-examples-core/cdk/app/events/event.json deleted file mode 100644 index 3822fadaa..000000000 --- a/examples/powertools-examples-core/cdk/app/events/event.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "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" - } - } - \ No newline at end of file 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 efc24a0ee..000000000 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ /dev/null @@ -1,241 +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> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml - <version>2.0.0-SNAPSHOT</version> - <artifactId>powertools-examples-core-utilities-sam</artifactId> - <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics) with SAM</name> -======== - <version>1.16.1</version> - <artifactId>powertools-examples-core-cdk</artifactId> - <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml - - <properties> - <log4j.version>2.20.0</log4j.version> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml - <aspectj.version>1.9.20</aspectj.version> -======== ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml - </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.3</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml - <version>3.11.2</version> -======== - <version>3.11.3</version> ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml - </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> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml - <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> - <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> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> -======== - <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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml - </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> - </profile> - </profiles> -</project> diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java deleted file mode 100644 index 988da2a73..000000000 --- a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.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 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; -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.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; -import software.amazon.lambda.powertools.metrics.Metrics; -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 final static Logger log = LogManager.getLogger(App.class); - - @Logging(logEvent = true, samplingRate = 0.7) - @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - @Metrics(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); - - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); - - LoggingUtils.appendKey("test", "willBeLogged"); - - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() - .withHeaders(headers); - try { - final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); - log.info(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 (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/cdk/app/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java deleted file mode 100644 index 401ef8c48..000000000 --- a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java +++ /dev/null @@ -1,38 +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.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.util.Map; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class AppStream implements RequestStreamHandler { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); - - System.out.println(map.size()); - } -} diff --git a/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml b/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml deleted file mode 100644 index e1fd14cea..000000000 --- a/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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> \ No newline at end of file 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/cdk.json b/examples/powertools-examples-core/cdk/infra/cdk.json deleted file mode 100644 index e24ee7b04..000000000 --- a/examples/powertools-examples-core/cdk/infra/cdk.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "app": "mvn -e -q compile exec:java", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "target", - "pom.xml", - "src/test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true - } -} diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml deleted file mode 100644 index 216bf35d4..000000000 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" - xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>software.amazon.lambda.examples</groupId> - <artifactId>cdk</artifactId> - <version>1.18.0-SNAPSHOT</version> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.100.0</cdk.version> - <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.10.0</junit.version> - </properties> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.11.0</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - </configuration> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <version>3.1.0</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> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> - </build> - <dependencies> - <!-- AWS Cloud Development Kit --> - <dependency> - <groupId>software.amazon.awscdk</groupId> - <artifactId>aws-cdk-lib</artifactId> - <version>${cdk.version}</version> - </dependency> - <dependency> - <groupId>software.constructs</groupId> - <artifactId>constructs</artifactId> - <version>${constructs.version}</version> - </dependency> - - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter</artifactId> - <version>${junit.version}</version> - <scope>test</scope> - </dependency> - </dependencies> -</project> diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java deleted file mode 100644 index d564eb9a0..000000000 --- a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.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 cdk; - -import software.amazon.awscdk.App; -import software.amazon.awscdk.StackProps; - -public class CdkApp { - public static void main(final String[] args) { - App app = new App(); - - new CdkStack(app, "CdkStack", StackProps.builder() - // If you don't specify 'env', this stack will be environment-agnostic. - // Account/Region-dependent features and context lookups will not work, - // but a single synthesized template can be deployed anywhere. - - // Uncomment the next block to specialize this stack for the AWS Account - // and Region that are implied by the current CLI configuration. - /* - .env(Environment.builder() - .account(System.getenv("CDK_DEFAULT_ACCOUNT")) - .region(System.getenv("CDK_DEFAULT_REGION")) - .build()) - */ - - - // Uncomment the next block if you know exactly what Account and Region you - // want to deploy the stack to. - /* - .env(Environment.builder() - .account("1234567890") - .region("region") - .build()) - */ - - // For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html - .build()); - - app.synth(); - } -} diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java deleted file mode 100644 index 8e6b44112..000000000 --- a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.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 cdk; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import software.amazon.awscdk.BundlingOptions; -import software.amazon.awscdk.CfnOutput; -import software.amazon.awscdk.Duration; -import software.amazon.awscdk.Stack; -import software.amazon.awscdk.StackProps; -import software.amazon.awscdk.services.apigateway.LambdaIntegration; -import software.amazon.awscdk.services.apigateway.RestApi; -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.Tracing; -import software.amazon.awscdk.services.s3.assets.AssetOptions; -import software.constructs.Construct; - -/** - * Defines a stack that consists of a single Java Lambda function and an API Gateway - */ -public class CdkStack extends Stack { - private static final String SHELL_COMMAND = "/bin/sh"; - private static final String MAVEN_PACKAGE = "mvn package"; - private static final String COPY_OUTPUT = "cp /asset-input/target/helloworld-lambda.jar /asset-output/"; - - public CdkStack(final Construct scope, final String id) { - this(scope, id, null); - } - - public CdkStack(final Construct scope, final String id, final StackProps props) { - super(scope, id, props); - - Function helloWorldFunction = createHelloWorldFunction(); - Function helloWorldStreamFunction = createHelloWorldStreamFunction(); - RestApi restApi = createHelloWorldApi(); - - restApi.getRoot().resourceForPath("/hello") - .addMethod("GET", LambdaIntegration.Builder.create(helloWorldFunction) - .build()); - - restApi.getRoot().resourceForPath("/hellostream") - .addMethod("GET", LambdaIntegration.Builder.create(helloWorldStreamFunction) - .build()); - - outputApiUrl(restApi); - } - - private static List<String> createFunctionPackageInstructions() { - // CDK will use this command to package your Java Lambda - return Arrays.asList( - SHELL_COMMAND, - "-c", - MAVEN_PACKAGE + " && " + - COPY_OUTPUT - ); - } - - /** - * Adds API URL to the outputs - * - * @param restApi - */ - private void outputApiUrl(RestApi restApi) { - CfnOutput.Builder.create(this, "HelloWorldApiUrl") - .description("API Gateway endpoint URL for Prod stage for Hello World function") - .value(restApi.getUrl() + "hello").build(); - } - - // Method to create the Lambda function - private Function createHelloWorldFunction() { - List<String> functionPackageInstructions = createFunctionPackageInstructions(); - - Map<String, String> environment = new HashMap<>(); - environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); - environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.1"); - environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); - environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); - - return Function.Builder.create(this, "HelloWorldFunction") - .runtime(Runtime.JAVA_11) - .memorySize(512) - .timeout(Duration.seconds(20)) - .tracing(Tracing.ACTIVE) - .code(Code.fromAsset("../app/", AssetOptions.builder() - .bundling(BundlingOptions.builder() - .image(Runtime.JAVA_11.getBundlingImage()) - .command(functionPackageInstructions) - .build()) - .build())) - .handler("helloworld.App") - .environment(environment) - .build(); - } - - private Function createHelloWorldStreamFunction() { - List<String> functionPackageInstructions = createFunctionPackageInstructions(); - - Map<String, String> environment = new HashMap<>(); - environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); - environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.7"); - environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); - environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); - environment.put("POWERTOOLS_SERVICE_NAME", "hello"); - - return Function.Builder.create(this, "HelloWorldStreamFunction") - .runtime(Runtime.JAVA_11) - .memorySize(512) - .timeout(Duration.seconds(20)) - .tracing(Tracing.ACTIVE) - .code(Code.fromAsset("../app/", AssetOptions.builder() - .bundling(BundlingOptions.builder() - .image(Runtime.JAVA_11.getBundlingImage()) - .command(functionPackageInstructions) - .build()) - .build())) - .handler("helloworld.AppStream") - .environment(environment) - .build(); - } - - // Method to create the REST API - private RestApi createHelloWorldApi() { - return RestApi.Builder.create(this, "HelloWorldApi") - .description("API Gateway endpoint URL for Prod stage for Hello World function") - .build(); - } -} 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/gradle/README.md b/examples/powertools-examples-core/gradle/README.md deleted file mode 100644 index c7c798d5d..000000000 --- a/examples/powertools-examples-core/gradle/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# 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 1.8 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/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle deleted file mode 100644 index e7367c246..000000000 --- a/examples/powertools-examples-core/gradle/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ - -plugins { - id 'java' - id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" -} - -wrapper { - gradleVersion = "7.6.1" -} - -compileJava { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - - ajc { - enabled = true - } -} - -repositories { - 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.11.0' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' - aspect 'software.amazon.lambda:powertools-tracing:1.17.0' - aspect 'software.amazon.lambda:powertools-logging:1.17.0' - aspect 'software.amazon.lambda:powertools-metrics:1.17.0' - testImplementation 'junit:junit:4.13.2' -} - diff --git a/examples/powertools-examples-core/gradle/events/event.json b/examples/powertools-examples-core/gradle/events/event.json deleted file mode 100644 index 070ad8e01..000000000 --- a/examples/powertools-examples-core/gradle/events/event.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "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/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore deleted file mode 100644 index 59c09e205..000000000 --- a/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -!gradle-wrapper.jar - diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 033e24c4cdf41af1ab109bc7f253b2b887023340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63375 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≫L+crb^Bz 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 E0aA=+Q2+n{ diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 0d23ac00c..000000000 --- a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/examples/powertools-examples-core/gradle/gradlew b/examples/powertools-examples-core/gradle/gradlew deleted file mode 100755 index fcb6fca14..000000000 --- a/examples/powertools-examples-core/gradle/gradlew +++ /dev/null @@ -1,248 +0,0 @@ -#!/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/gradle/gradlew.bat b/examples/powertools-examples-core/gradle/gradlew.bat deleted file mode 100644 index 6689b85be..000000000 --- a/examples/powertools-examples-core/gradle/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@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/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java deleted file mode 100644 index fccc63b9a..000000000 --- a/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.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 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; -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.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; -import software.amazon.lambda.powertools.metrics.Metrics; -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 final static Logger log = LogManager.getLogger(App.class); - - @Logging(logEvent = true, samplingRate = 0.7) - @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - @Metrics(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); - - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); - - LoggingUtils.appendKey("test", "willBeLogged"); - - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() - .withHeaders(headers); - try { - final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); - log.info(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/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java deleted file mode 100644 index 401ef8c48..000000000 --- a/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java +++ /dev/null @@ -1,38 +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.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.util.Map; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class AppStream implements RequestStreamHandler { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); - - System.out.println(map.size()); - } -} diff --git a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java deleted file mode 100644 index af3ec1275..000000000 --- a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package helloworld; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class AppTest { - @Test - public void successfulResponse() { - App app = new App(); - APIGatewayProxyResponseEvent result = app.handleRequest(null, null); - assertEquals(200, result.getStatusCode().intValue()); - assertEquals("application/json", result.getHeaders().get("Content-Type")); - 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/gradle/template.yaml b/examples/powertools-examples-core/gradle/template.yaml deleted file mode 100644 index a717c2998..000000000 --- a/examples/powertools-examples-core/gradle/template.yaml +++ /dev/null @@ -1,71 +0,0 @@ -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: java8 - 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: java8 - 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/sam/README.md b/examples/powertools-examples-core/sam/README.md deleted file mode 100644 index 7a4279ae3..000000000 --- a/examples/powertools-examples-core/sam/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example with SAM - -This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/). - -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) - -## 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/sam/events/event.json b/examples/powertools-examples-core/sam/events/event.json deleted file mode 100644 index 3822fadaa..000000000 --- a/examples/powertools-examples-core/sam/events/event.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "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" - } - } - \ No newline at end of file diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml deleted file mode 100644 index 93c858556..000000000 --- a/examples/powertools-examples-core/sam/pom.xml +++ /dev/null @@ -1,207 +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.18.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> - </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.3</version> - </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - <!-- Don't deploy the examples --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <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> - <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/main/java/helloworld/App.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java deleted file mode 100644 index fccc63b9a..000000000 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.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 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; -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.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; -import software.amazon.lambda.powertools.metrics.Metrics; -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 final static Logger log = LogManager.getLogger(App.class); - - @Logging(logEvent = true, samplingRate = 0.7) - @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - @Metrics(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); - - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); - - LoggingUtils.appendKey("test", "willBeLogged"); - - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() - .withHeaders(headers); - try { - final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); - log.info(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/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java deleted file mode 100644 index 401ef8c48..000000000 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ /dev/null @@ -1,38 +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.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.util.Map; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class AppStream implements RequestStreamHandler { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); - - System.out.println(map.size()); - } -} diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml deleted file mode 100644 index e1fd14cea..000000000 --- a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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> \ No newline at end of file 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-core/sam/template.yaml b/examples/powertools-examples-core/sam/template.yaml deleted file mode 100644 index 9a51a1ba9..000000000 --- a/examples/powertools-examples-core/sam/template.yaml +++ /dev/null @@ -1,66 +0,0 @@ -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: . - 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/serverless/README.md b/examples/powertools-examples-core/serverless/README.md deleted file mode 100644 index aec093182..000000000 --- a/examples/powertools-examples-core/serverless/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# 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/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml deleted file mode 100644 index 793318da3..000000000 --- a/examples/powertools-examples-core/serverless/pom.xml +++ /dev/null @@ -1,209 +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.18.0-SNAPSHOT</version> - <artifactId>powertools-examples-core-serverless</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> - </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> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <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> - <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/serverless/serverless.yml b/examples/powertools-examples-core/serverless/serverless.yml deleted file mode 100644 index d8dec8080..000000000 --- a/examples/powertools-examples-core/serverless/serverless.yml +++ /dev/null @@ -1,40 +0,0 @@ -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/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java deleted file mode 100644 index 727e048f2..000000000 --- a/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java +++ /dev/null @@ -1,117 +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 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; -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.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; -import software.amazon.lambda.powertools.metrics.Metrics; -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 final static Logger log = LogManager.getLogger(App.class); - - // This is controlled by POWERTOOLS_LOGGER_SAMPLE_RATE environment variable - // @Logging(logEvent = true, samplingRate = 0.7) - // This is controlled by POWERTOOLS_METRICS_NAMESPACE environment variable - // @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - // This is controlled by POWERTOOLS_TRACER_CAPTURE_ERROR environment variable - @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) - 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); - - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> - { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); - - LoggingUtils.appendKey("test", "willBeLogged"); - - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() - .withHeaders(headers); - try { - final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); - log.info(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); -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java - } catch (IOException e) { -======== - } catch (RuntimeException | IOException e) { ->>>>>>>> main:examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java - return response - .withBody("{}") - .withStatusCode(500); - } - } - -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java - @Tracing - private void log() { - log.info("inside threaded logging for function"); - } - -======== ->>>>>>>> main:examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java - @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/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java deleted file mode 100644 index 401ef8c48..000000000 --- a/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java +++ /dev/null @@ -1,38 +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.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.util.Map; -import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.metrics.Metrics; - -public class AppStream implements RequestStreamHandler { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); - - System.out.println(map.size()); - } -} diff --git a/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml deleted file mode 100644 index 0cc0953f0..000000000 --- a/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 70dad8d71..000000000 --- a/examples/powertools-examples-core/serverless/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\"")); - } -} From 533ac9a4aeab3ddfc7b191214dfa6be0a5d62489 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:13:40 +0200 Subject: [PATCH 0501/1008] build(deps-dev): bump com.amazonaws:amazon-sqs-java-extended-client-lib (#1476) Bumps [com.amazonaws:amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases) - [Commits](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/compare/2.0.3...2.0.4) --- updated-dependencies: - dependency-name: com.amazonaws:amazon-sqs-java-extended-client-lib dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 89a255c8b..0a760bd0d 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -84,7 +84,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - <version>2.0.3</version> + <version>2.0.4</version> <scope>test</scope> </dependency> From fc9f0ec03638bafd19a92914056bc5756d5954a4 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Fri, 13 Oct 2023 17:46:32 +0200 Subject: [PATCH 0502/1008] Resolve merge issues --- examples/pom.xml | 8 +- examples/powertools-examples-batch/pom.xml | 2 + .../pom.xml | 4 +- .../cdk/README.md | 10 +- .../cdk/app/pom.xml | 147 ++++++++--------- .../cdk/app/src/main/java/helloworld/App.java | 14 +- .../gradle/README.md | 2 +- .../sam/pom.xml | 88 +--------- .../serverless/README.md | 26 +++ .../serverless/pom.xml | 155 ++++++++++++++++++ .../serverless/serverless.yml | 40 +++++ .../src/main/java/helloworld/App.java | 99 +++++++++++ .../src/main/java/helloworld/AppStream.java | 38 +++++ .../serverless/src/main/resources/log4j2.xml | 16 ++ .../src/test/java/helloworld/AppTest.java | 59 +++++++ .../powertools-examples-idempotency/pom.xml | 16 +- .../powertools-examples-parameters/pom.xml | 11 +- .../powertools-examples-validation/pom.xml | 21 ++- powertools-e2e-tests/handlers/pom.xml | 11 +- powertools-e2e-tests/pom.xml | 3 +- 20 files changed, 562 insertions(+), 208 deletions(-) create mode 100644 examples/powertools-examples-core-utilities/serverless/README.md create mode 100644 examples/powertools-examples-core-utilities/serverless/pom.xml create mode 100644 examples/powertools-examples-core-utilities/serverless/serverless.yml create mode 100644 examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core-utilities/serverless/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core-utilities/serverless/src/test/java/helloworld/AppTest.java diff --git a/examples/pom.xml b/examples/pom.xml index baa07eb5e..be52778da 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,10 +29,10 @@ </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-core/serverless</module> + <module>powertools-examples-core-utilities/sam</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-idempotency</module> <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 35984ef12..a3eb332f4 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -15,6 +15,8 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <sdk.version>2.20.152</sdk.version> + <aspectj.version>1.9.20</aspectj.version> + </properties> <dependencies> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 6b4e850e1..cc95a2dfc 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.17.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> @@ -16,6 +16,8 @@ <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> <aws.sdk.version>2.20.162</aws.sdk.version> + <aspectj.version>1.9.20</aspectj.version> + </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-core-utilities/cdk/README.md b/examples/powertools-examples-core-utilities/cdk/README.md index acd857ed7..f15a24168 100644 --- a/examples/powertools-examples-core-utilities/cdk/README.md +++ b/examples/powertools-examples-core-utilities/cdk/README.md @@ -6,11 +6,11 @@ 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 - - [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. +- [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. 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. diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 51c222a07..07e9d126f 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -3,11 +3,12 @@ <modelVersion>4.0.0</modelVersion> <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>1.17.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics) with CDK</name> + <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <properties> <log4j.version>2.20.0</log4j.version> @@ -40,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -67,80 +68,72 @@ </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> - <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> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <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.1.0</version> - </dependency> - </dependencies> - </plugin> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> + <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> + <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> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <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.1.0</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 diff --git a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java index 727e048f2..988da2a73 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java @@ -46,12 +46,9 @@ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { private final static Logger log = LogManager.getLogger(App.class); - // This is controlled by POWERTOOLS_LOGGER_SAMPLE_RATE environment variable - // @Logging(logEvent = true, samplingRate = 0.7) - // This is controlled by POWERTOOLS_METRICS_NAMESPACE environment variable - // @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - // This is controlled by POWERTOOLS_TRACER_CAPTURE_ERROR environment variable + @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); @@ -87,25 +84,18 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv return response .withStatusCode(200) .withBody(output); -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java } catch (IOException e) { -======== - } catch (RuntimeException | IOException e) { ->>>>>>>> main:examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java return response .withBody("{}") .withStatusCode(500); } } -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java @Tracing private void log() { log.info("inside threaded logging for function"); } -======== ->>>>>>>> main:examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java @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-utilities/gradle/README.md b/examples/powertools-examples-core-utilities/gradle/README.md index cd6107151..c7c798d5d 100644 --- a/examples/powertools-examples-core-utilities/gradle/README.md +++ b/examples/powertools-examples-core-utilities/gradle/README.md @@ -8,7 +8,7 @@ You can also use `sam init` to create a new Gradle-powered Powertools applicatio 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](../../powertools-examples-core/README.md) +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. diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index efc24a0ee..130fb814d 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -2,29 +2,17 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics) with SAM</name> -======== - <version>1.16.1</version> - <artifactId>powertools-examples-core-cdk</artifactId> - <packaging>jar</packaging> - - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml <properties> <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml <aspectj.version>1.9.20</aspectj.version> -======== ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml </properties> <dependencies> @@ -51,11 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml - <version>3.11.2</version> -======== <version>3.11.3</version> ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -82,7 +66,6 @@ </dependencies> <build> -<<<<<<<< HEAD:examples/powertools-examples-core-utilities/sam/pom.xml <plugins> <plugin> <groupId>dev.aspectj</groupId> @@ -148,75 +131,6 @@ </dependencies> </plugin> </plugins> -======== - <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="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.1.0</version> - </dependency> - </dependencies> - </plugin> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> ->>>>>>>> main:examples/powertools-examples-core/cdk/app/pom.xml </build> <profiles> <!-- Use a profile to enforce AspectJ version 1.9.7 if we are Java 1.8 otherwise we'll get class 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..aec093182 --- /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..013f8bd7d --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -0,0 +1,155 @@ +<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) library Examples - Core Utilities (logging, tracing, metrics) with SAM</name> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.0.0-SNAPSHOT</version> + <artifactId>powertools-examples-core-utilities-serverless</artifactId> + <packaging>jar</packaging> + + <properties> + <log4j.version>2.20.0</log4j.version> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</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.2.3</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.3</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> + + <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> + <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> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <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.1.0</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> + </profile> + </profiles> +</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-utilities/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java new file mode 100644 index 000000000..c89545fc9 --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + 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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core-utilities/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/serverless/src/main/resources/log4j2.xml new file mode 100644 index 000000000..0cc0953f0 --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/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-utilities/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core-utilities/serverless/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..70dad8d71 --- /dev/null +++ b/examples/powertools-examples-core-utilities/serverless/src/test/java/helloworld/AppTest.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 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 index 849d3764b..b97eee7cc 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -15,7 +15,7 @@ <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.0.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> @@ -26,6 +26,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -52,7 +53,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -127,6 +128,13 @@ </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> @@ -173,8 +181,7 @@ </goals> <configuration> <transformers> - <transformer - implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> </configuration> </execution> @@ -187,7 +194,6 @@ </dependency> </dependencies> </plugin> - <!-- Don't deploy the example --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 6ca0c33bf..81081035c 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -10,6 +10,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -31,7 +32,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.2</version> </dependency> <dependency> <groupId>org.aspectj</groupId> @@ -90,8 +91,14 @@ </goals> </execution> </executions> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> </plugin> - <!-- Don't deploy the example --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 8551e02a2..679e698e9 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -24,6 +24,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -42,6 +43,11 @@ <artifactId>aws-lambda-java-core</artifactId> <version>1.2.3</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> <!-- Test dependencies --> <dependency> @@ -94,14 +100,13 @@ </goals> </execution> </executions> - </plugin> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <version>${aspectj.version}</version> + </dependency> + </dependencies> </plugin> </plugins> </build> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index db0236146..fbe2e6d8b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -23,6 +23,7 @@ <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> + <aspectj.version>1.9.20</aspectj.version> </properties> <modules> @@ -131,11 +132,11 @@ </execution> </executions> <dependencies> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> - </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.1.0</version> + </dependency> </dependencies> </plugin> <plugin> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index cb365cc72..70c3a4105 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -32,6 +32,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.69</constructs.version> <cdk.version>2.93.0</cdk.version> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -40,7 +41,6 @@ <artifactId>log4j-slf4j2-impl</artifactId> <scope>test</scope> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>lambda</artifactId> @@ -165,6 +165,7 @@ <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> <scope>compile</scope> </dependency> <dependency> From 653e98adb9bd066fb28960a1017b26566a677b9b Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Fri, 13 Oct 2023 17:53:53 +0200 Subject: [PATCH 0503/1008] Fix aspectj issue --- powertools-e2e-tests/pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 70c3a4105..cb365cc72 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -32,7 +32,6 @@ <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.2.69</constructs.version> <cdk.version>2.93.0</cdk.version> - <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -41,6 +40,7 @@ <artifactId>log4j-slf4j2-impl</artifactId> <scope>test</scope> </dependency> + <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>lambda</artifactId> @@ -165,7 +165,6 @@ <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> - <version>${aspectj.version}</version> <scope>compile</scope> </dependency> <dependency> From 0fe0d2c4a2fd958d9ed6b35e28a1af386b5be5d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:01:04 +0200 Subject: [PATCH 0504/1008] build(deps): bump aws.sdk.version from 2.20.162 to 2.21.0 (#1480) Bumps `aws.sdk.version` from 2.20.162 to 2.21.0. Updates `software.amazon.awssdk:bom` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:http-client-spi` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:url-connection-client` from 2.20.152 to 2.21.0 Updates `software.amazon.awssdk:sqs` from 2.20.152 to 2.21.0 Updates `software.amazon.awssdk:s3` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:dynamodb` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:lambda` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:kinesis` from 2.20.152 to 2.21.0 Updates `software.amazon.awssdk:cloudwatch` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:xray` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:cloudformation` from 2.20.162 to 2.21.0 Updates `software.amazon.awssdk:sts` from 2.20.162 to 2.21.0 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c696ef0a8..01aedcaaf 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.20.162</aws.sdk.version> + <aws.sdk.version>2.21.0</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 988291413..52766608a 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.20.162</version> + <version>2.21.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index fd31bef37..be696094f 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <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.162</aws.sdk.version> + <aws.sdk.version>2.21.0</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 857fd4cb706a7a81fda0cfc6be43600d05087574 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Mon, 16 Oct 2023 20:34:29 +0200 Subject: [PATCH 0505/1008] Fix up merge --- examples/powertools-examples-batch/pom.xml | 2 +- .../cdk/app/pom.xml | 141 ++++++++++-------- .../powertools-examples-idempotency/pom.xml | 2 +- .../powertools-examples-parameters/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/pom.xml | 4 +- 6 files changed, 81 insertions(+), 72 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index a3eb332f4..1387cca6d 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.152</sdk.version> + <sdk.version>2.20.162</sdk.version> <aspectj.version>1.9.20</aspectj.version> </properties> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 07e9d126f..891a7c7f6 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -68,72 +68,81 @@ </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> - <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> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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> + <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> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <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 diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index b97eee7cc..02fca7e98 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -53,7 +53,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 81081035c..cffab7a65 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -32,7 +32,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/pom.xml b/pom.xml index 4b79a126f..8fb9f5d04 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <lambda.core.version>1.2.2</lambda.core.version> + <lambda.core.version>1.2.3</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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index cb365cc72..b150a179e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -30,8 +30,8 @@ <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.93.0</cdk.version> + <constructs.version>10.2.70</constructs.version> + <cdk.version>2.100.0</cdk.version> </properties> <dependencies> From 51930d0f67d99764a00c5043b3f6c889d937e80e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:31:11 +0200 Subject: [PATCH 0506/1008] build(deps): bump aws.sdk.version from 2.20.152 to 2.21.1 (#1481) Bumps `aws.sdk.version` from 2.20.152 to 2.21.1. Updates `software.amazon.awssdk:url-connection-client` from 2.20.152 to 2.21.1 Updates `software.amazon.awssdk:sqs` from 2.20.152 to 2.21.1 Updates `software.amazon.awssdk:sdk-core` from 2.20.152 to 2.21.1 Updates `software.amazon.awssdk:kinesis` from 2.20.152 to 2.21.1 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.20.152 to 2.21.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index ea5d1e5f1..bbdc19b9f 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <sdk.version>2.20.152</sdk.version> + <sdk.version>2.21.1</sdk.version> </properties> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 52766608a..889fe85da 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.21.0</version> + <version>2.21.1</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 7f4846daeb06a40453c74bd96c84f47ee8a75d43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:05:40 +0200 Subject: [PATCH 0507/1008] build(deps-dev): bump software.constructs:constructs (#1482) Bumps [software.constructs:constructs](https://github.com/aws/constructs) from 10.2.70 to 10.3.0. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.2.70...v10.3.0) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0a760bd0d..84760eac8 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -30,7 +30,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <constructs.version>10.2.70</constructs.version> + <constructs.version>10.3.0</constructs.version> <cdk.version>2.100.0</cdk.version> </properties> From e9d6b8fb73cf18bda8a6ec74d1994acb288baa64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:39:52 +0200 Subject: [PATCH 0508/1008] Update dependabot.yml --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4832b1c49..1454df79c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,11 +3,11 @@ updates: - package-ecosystem: "maven" directory: "/" schedule: - interval: "daily" + interval: "weekly" 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 + update-types: ["version-update:semver-major"] From ac90f33664d079c78da09f562f6ea5e8107287f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:50:51 +0200 Subject: [PATCH 0509/1008] build(deps): bump jackson.version from 2.15.2 to 2.15.3 (#1483) Bumps `jackson.version` from 2.15.2 to 2.15.3. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.15.2 to 2.15.3 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.15.2 to 2.15.3 - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.15.2...jackson-core-2.15.3) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.15.2 to 2.15.3 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be696094f..823b09d61 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <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> + <jackson.version>2.15.3</jackson.version> <aspectj.version>1.9.7</aspectj.version> <aws.sdk.version>2.21.0</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> From 5e3a4e63d7cd4b8d505b50f7ef2b36b8cac1e549 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:51:17 +0200 Subject: [PATCH 0510/1008] build(deps): bump com.amazonaws:aws-lambda-java-core from 1.2.2 to 1.2.3 (#1460) Bumps [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs) from 1.2.2 to 1.2.3. --- examples/powertools-examples-core/serverless/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 793318da3..faa47c591 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -34,7 +34,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 34fc20eb91d893384aa60254a310601e6b6fa5db Mon Sep 17 00:00:00 2001 From: skal111 <p_romanens@hotmail.com> Date: Mon, 23 Oct 2023 09:21:08 +0200 Subject: [PATCH 0511/1008] feat: Terraform example (#1478) --- .github/workflows/pr_build.yml | 29 +++ .gitignore | 4 +- examples/pom.xml | 1 + examples/powertools-examples-core/README.md | 1 + .../terraform/.tflint.hcl | 3 + .../terraform/README.md | 27 +++ .../terraform/infra/api-gateway.tf | 94 ++++++++ .../terraform/infra/lambda.tf | 95 ++++++++ .../terraform/main.tf | 22 ++ .../terraform/pom.xml | 217 ++++++++++++++++++ .../src/main/java/helloworld/App.java | 105 +++++++++ .../src/main/java/helloworld/AppStream.java | 38 +++ .../terraform/src/main/resources/log4j2.xml | 16 ++ .../src/test/java/helloworld/AppTest.java | 38 +++ 14 files changed, 689 insertions(+), 1 deletion(-) create mode 100644 examples/powertools-examples-core/terraform/.tflint.hcl create mode 100644 examples/powertools-examples-core/terraform/README.md create mode 100644 examples/powertools-examples-core/terraform/infra/api-gateway.tf create mode 100644 examples/powertools-examples-core/terraform/infra/lambda.tf create mode 100644 examples/powertools-examples-core/terraform/main.tf create mode 100644 examples/powertools-examples-core/terraform/pom.xml create mode 100644 examples/powertools-examples-core/terraform/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/terraform/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/terraform/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.java diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 73ae67553..79945f237 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -53,6 +53,9 @@ jobs: env: JAVA: ${{ matrix.java }} AWS_REGION: eu-west-1 + permissions: + id-token: write # needed to interact with GitHub's OIDC Token endpoint. + contents: read steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java @@ -68,6 +71,32 @@ jobs: run: | cd examples/powertools-examples-core/gradle ./gradlew build + - name: Setup Terraform + if: ${{ matrix.java == '11' }} + uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} + aws-region: ${{ env.AWS_REGION }} + - name: Terraform validate + if: ${{ matrix.java == '11' }} + run: | + terraform -version + cd examples/powertools-examples-core/terraform + terraform init -backend=false + terraform validate + terraform plan + - name: Setup Terraform lint + if: ${{ matrix.java == '11' }} + uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 + - name: Terraform lint + if: ${{ matrix.java == '11' }} + run: | + tflint --version + cd examples/powertools-examples-core/terraform + tflint --init + tflint -f compact - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once diff --git a/.gitignore b/.gitignore index b404d2cb2..6615ac729 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,6 @@ example/HelloWorldFunction/build /example/.gradle/ /example/.java-version .gradle -build/ \ No newline at end of file +build/ +.terraform* +terraform.tfstate* \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 810ec1b36..900f095f8 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -33,6 +33,7 @@ <module>powertools-examples-core/cdk/app</module> <module>powertools-examples-core/cdk/infra</module> <module>powertools-examples-core/serverless</module> + <module>powertools-examples-core/terraform</module> <module>powertools-examples-idempotency</module> <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 1d1dd031f..a0ffb125d 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -10,6 +10,7 @@ We provide examples for the following infrastructure-as-code tools: * [AWS SAM](sam/) * [AWS CDK](cdk/) * [Serverless framework](serverless/) +* [Terraform](terraform/) We also provide an example showing the integration of SAM, Powertools, and Gradle: diff --git a/examples/powertools-examples-core/terraform/.tflint.hcl b/examples/powertools-examples-core/terraform/.tflint.hcl new file mode 100644 index 000000000..18e69352b --- /dev/null +++ b/examples/powertools-examples-core/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/terraform/README.md b/examples/powertools-examples-core/terraform/README.md new file mode 100644 index 000000000..71c78d437 --- /dev/null +++ b/examples/powertools-examples-core/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/terraform/infra/api-gateway.tf b/examples/powertools-examples-core/terraform/infra/api-gateway.tf new file mode 100644 index 000000000..dba1c3616 --- /dev/null +++ b/examples/powertools-examples-core/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/terraform/infra/lambda.tf b/examples/powertools-examples-core/terraform/infra/lambda.tf new file mode 100644 index 000000000..abebccf54 --- /dev/null +++ b/examples/powertools-examples-core/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/terraform/main.tf b/examples/powertools-examples-core/terraform/main.tf new file mode 100644 index 000000000..10504088a --- /dev/null +++ b/examples/powertools-examples-core/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/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml new file mode 100644 index 000000000..8da2780d8 --- /dev/null +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -0,0 +1,217 @@ +<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.18.0-SNAPSHOT</version> + <artifactId>powertools-examples-core-terraform</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> + </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="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.1.0</version> + </dependency> + </dependencies> + </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <environmentVariables> + <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> + </environmentVariables> + </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> + <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/terraform/src/main/java/helloworld/App.java b/examples/powertools-examples-core/terraform/src/main/java/helloworld/App.java new file mode 100644 index 000000000..dacd7f1d4 --- /dev/null +++ b/examples/powertools-examples-core/terraform/src/main/java/helloworld/App.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 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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +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 final static Logger log = LogManager.getLogger(App.class); + + // This is controlled by POWERTOOLS_LOGGER_SAMPLE_RATE environment variable + // @Logging(logEvent = true, samplingRate = 0.7) + // This is controlled by POWERTOOLS_METRICS_NAMESPACE environment variable + // @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // This is controlled by POWERTOOLS_TRACER_CAPTURE_ERROR environment variable + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + 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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(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/terraform/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/terraform/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/terraform/src/main/java/helloworld/AppStream.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 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.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/terraform/src/main/resources/log4j2.xml b/examples/powertools-examples-core/terraform/src/main/resources/log4j2.xml new file mode 100644 index 000000000..0cc0953f0 --- /dev/null +++ b/examples/powertools-examples-core/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/terraform/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..0ca4f264d --- /dev/null +++ b/examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.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 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 org.junit.Test; + +public class AppTest { + + @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\"")); + } +} From aed398bd0a99165dc8614147a921393775571af7 Mon Sep 17 00:00:00 2001 From: Jason Harris <jiharris90@gmail.com> Date: Mon, 23 Oct 2023 19:18:33 +0100 Subject: [PATCH 0512/1008] docs: Adding Kotlin example. (#1454) * Setting up Kotlin environment. Converting test to Kotlin. * Deploying via SAM successfully. * Added Kotlin example. * Removing unused Gradle build file. * Adding SAM template so can be used as an existing project and Java target compatibility * Adding SAM template so can be used as an existing project * Updating guidance to use SAM for build and deploy * Restructuring separate Java and Kotlin examples. * Updating core examples readme to represent new structure for Java and Kotlin examples. * Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive * Updating to fix trailing \n * Updating guidance to be more specific for examples * Adopting new mechanism for specifying jvm target. * accommodating new project structure * Fixing link typo after refactoring * Setting up Kotlin environment. Converting test to Kotlin. * Deploying via SAM successfully. * Added Kotlin example. * Removing unused Gradle build file. * Adding SAM template so can be used as an existing project and Java target compatibility * Adding SAM template so can be used as an existing project * Updating guidance to use SAM for build and deploy * Restructuring separate Java and Kotlin examples. * Updating core examples readme to represent new structure for Java and Kotlin examples. * Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive * Updating to fix trailing \n * Updating guidance to be more specific for examples * Adopting new mechanism for specifying jvm target. * accommodating new project structure * Fixing link typo after refactoring * Flattening structure back to original to make merging easier for v2 * Adding build for Kotlin Gradle * Adding build for Kotlin Gradle - Restructuring Java examples to v1 approach * Correcting paths * Adding SNAPSHOT support and local capability for Maven. Testing using Java 1.8 * Reviewed and updated against PR comments. * Un-commenting examples --------- Co-authored-by: Jason Harris <harrzjas@amazon.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- .github/workflows/pr_build.yml | 6 +- examples/README.md | 6 +- examples/powertools-examples-core/README.md | 36 ++- .../powertools-examples-core/cdk/README.md | 6 +- .../gradle/gradlew.bat | 184 ++++++------- .../powertools-examples-core/kotlin/README.md | 38 +++ .../kotlin/build.gradle.kts | 39 +++ .../kotlin/events/event.json | 62 +++++ .../kotlin/gradle/wrapper/.gitignore | 2 + .../kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../powertools-examples-core/kotlin/gradlew | 248 ++++++++++++++++++ .../kotlin/gradlew.bat | 92 +++++++ .../kotlin/src/main/kotlin/helloworld/App.kt | 96 +++++++ .../src/main/kotlin/helloworld/AppStream.kt | 37 +++ .../src/test/kotlin/helloworld/AppTest.kt | 20 ++ .../kotlin/template.yaml | 71 +++++ .../serverless/README.md | 2 +- 18 files changed, 840 insertions(+), 112 deletions(-) create mode 100644 examples/powertools-examples-core/kotlin/README.md create mode 100644 examples/powertools-examples-core/kotlin/build.gradle.kts create mode 100644 examples/powertools-examples-core/kotlin/events/event.json create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/powertools-examples-core/kotlin/gradlew create mode 100644 examples/powertools-examples-core/kotlin/gradlew.bat create mode 100644 examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt create mode 100644 examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt create mode 100644 examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt create mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 79945f237..e99900ce5 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -66,11 +66,15 @@ jobs: cache: 'maven' - name: Build with Maven run: mvn -B install --file pom.xml - - name: Build Gradle Example + - name: Build Gradle Example - Java if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 run: | cd examples/powertools-examples-core/gradle ./gradlew build + - name: Build Gradle Example - Kotlin + run: | + cd examples/powertools-examples-core/kotlin + ./gradlew build - name: Setup Terraform if: ${{ matrix.java == '11' }} uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 diff --git a/examples/README.md b/examples/README.md index 52dc0c1e9..6dbe00185 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,10 +5,12 @@ Each example can be copied from its subdirectory and used independently of the r ## Examples -* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools - * [SAM](./powertools-examples-core/sam) +* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools and languages * [CDK](./powertools-examples-core/cdk) + * [Gradle](./powertools-examples-core/gradle) + * [SAM](./powertools-examples-core/sam) * [Serverless](./powertools-examples-core/serverless) + * [Kotlin](./powertools-examples-core/kotlin) * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index a0ffb125d..c9ca60f57 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,12 +1,13 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example +# 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: +The example application is the same, and you can now also use Kotlin! +## Java * [AWS SAM](sam/) * [AWS CDK](cdk/) * [Serverless framework](serverless/) @@ -16,11 +17,21 @@ We also provide an example showing the integration of SAM, Powertools, and Gradl * [AWS SAM with a Gradle build](gradle/) -For each of the tools, the example application is the same, and consists of the following files: +- 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. -- [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. + +## 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. @@ -32,14 +43,13 @@ Once the app is deployed, you can invoke the endpoint like this: 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 +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 +- **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). +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/cdk/README.md index f15a24168..fbc558943 100644 --- a/examples/powertools-examples-core/cdk/README.md +++ b/examples/powertools-examples-core/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/gradle/gradlew.bat b/examples/powertools-examples-core/gradle/gradlew.bat index 6689b85be..93e3f59f1 100644 --- a/examples/powertools-examples-core/gradle/gradlew.bat +++ b/examples/powertools-examples-core/gradle/gradlew.bat @@ -1,92 +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 +@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/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md new file mode 100644 index 000000000..da9ec5b52 --- /dev/null +++ b/examples/powertools-examples-core/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 1.8 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/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts new file mode 100644 index 000000000..fc363c1b9 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + id("io.freefair.aspectj.post-compile-weaving") version "6.6.3" + kotlin("jvm") version "1.9.10" +} + +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.11.0") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2") + aspect("software.amazon.lambda:powertools-tracing:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-logging:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-metrics:1.18.0-SNAPSHOT") + testImplementation("junit:junit:4.13.2") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") +} + +tasks.compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + +tasks.compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + +// If using JDK 11 or higher, use the following instead: +//kotlin { +// jvmToolchain(11) +//} \ No newline at end of file diff --git a/examples/powertools-examples-core/kotlin/events/event.json b/examples/powertools-examples-core/kotlin/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/examples/powertools-examples-core/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/kotlin/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore new file mode 100644 index 000000000..59c09e205 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore @@ -0,0 +1,2 @@ +!gradle-wrapper.jar + diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340 GIT binary patch literal 63375 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≫L+crb^Bz zc+Nw%zg<eW;A}s=*P6+gF}bio8=x0TEl%l4pJ$tyY5b9sQ8QUf<CVb&IosSO?U)TS zqRaFVMB?L$Va^G<K_IKy<}kIfB`>pZ6>!A3%lau!Pw6`Y#WPVBtAfKSsqwYDWQK-~ zz(mx=nJ6-8t`YXB{6gaZ%G}Dmn&o500Y}2Rd?e&@=hBEmB1C=$OMBfxX__2<amvr< zXa%T~J;`~)wa6K9vLDPZ4GZLPS7oKSy)VETgG@jr+mViaX=%jwAwMaxuIET{i2|{P z=%Yb3&*b&m#ml+5FlJql5a}W%z?`C^MKY$$m`pDfNwvint?IO6amJ*PZQL1(52tL{ zJANajfD2`9E?S2iDE{r9w1H+KbS!7BR1@VophCkXHR`|fTeaGAB8za0A1K7kCS(bA z3^hY;UdsU90Qq(v&N0T9JSv}(7&&Gw+V%U6EH!}fv*RqA&zDLjkb!uv6idVcvDYv} z&BaSl7_k9>c2O4K2#(0ksclP$SHp*8jq-1&(<6(#=6&H`Nlc2RVC4->r6U}sTY<1? zn@tv7XwUs-c>Lcmrm5AE0jHI5={WgHIow6cX=UK)>602(=arbuAPZ37;{HT<bASz# zhpNmfwQSDBB;fIIk_gW5U{}19wURbn{If{5IyR->JSIO%9EL`Et5%J7$u_NaC(55x zH^qX^H}*RPDx)^c46x>js=%&?y?=iFs^#_rUl@*MgLD92E5y4B7#EDe9yyn*f-|pQ zi>(!bIg6zY5fLSn@;$*sN|D2A{}we*7+2(4&EhUV%Qqo5=uuN^xt_hll7=`*mJq6s zCWUB|s$)AuS&=)T&_$w>QXHqCWB&ndQ$y4-9fezybZb0bYD^zeuZ>WZF{rc>c4s`` zgKdppTB|o>L1I1hAbnW%H%EkFt%yWC|0~+o7mIyFCTyb?@*Ho)eu(x`PuO8pLikN> z6YeI`V?AUWD(~3=8>}a6nZTu~#QCK(H0+4!ql3yS`>JX;j4+YkeG$ZTm33~PLa3L} zksw7@%e-mBM*cGfz$tS4LC^SYVdBLsR}nAprwg8h2~+Cv*W0%izK+WPVK}^SsL5R_ zpA}~G?VNhJhqx2he2;2$>7>DUB$wN9_-adL@TqVLe=*F8Vsw-yho@#mTD6*2WAr6B zjtLUh`E(;#p0-&$FVw(r$hn+5^Z~9J0}k;j$jL1;?2GN9s?}LASm?*Rvo@?E+(}F& z+=&M-n`5EIz%%F^e)nnWjkQUdG|W^~O|YeY4Fz}>qH2juEere}vN$oJN~9_Th^&b{ z%IBbET*E8%C@jLTxV~h#mxoRrJCF{!CJOghjuKOyl_!Jr?@4Upo7u>fTGtfm|CH2v z&9F+>;6aFbYXLj3{yZ~Yn1J2%!)A3~j2$`jOy{XavW@t)g}}KUVjCWG0OUc7aBc=2 zR3^u=dT47=5SmT{K1aGaVZkOx|24T-J0O$b9dfB25J|7yb6frwS6wZ1^y%EWOm}S< zc1SdYhfsdLG*FB-;!QLV3D!d~hnXTGVQVck9x%=B(Kk8c3y%f0nR95_TbY;l=obSl zEE@fp0|8Q$b3(+DXh?d0FEloGhO0#11CLQT5qtEckBLe-VN-I>9ys}PVK0r;0!jIG zH_q$;a`3Xv9P_V2ekV1SMzd#SKo<1~Dq2?M{(V;AwhH_2x@mN$=|=cG0<3o^j_0OF z7|WJ-f2G=7sA4NVGU2X5`o*D2T7(MbmZ2(oipooE{R?9!{WxX!%ofhsrPAxoIk!Kr z>I$a{Zq=%KaLrDCIL^gmA3z{2z%Wkr)b$QHcNUA^QwydWMJmxymO0QS22?mo%4(Md zgME(zE}ub--3*wGjV`3eBMCQG-@Gel1NKZDGuqobN|mA<Orshs+Cll$u%OVm+m7$A zvobiM4A4uVtI2;EQ`is0JxPx9*53^imsz^x6`T%eO>t0{@ZC9goI|BSmGBTUZ(`Xt z^e2LiMg?6E?G*yw(~K8lO(c4)RY7UWxrXzW^iCg-P41dUiE(i+gDmmAoB?XOB}+Ln z_}rApiR$sqNaT4frw69Wh4W?v(27IlK$Toy<1o)GeF+sGzYVeJ`F)3`&2WDi^_v67 zg;@ehwl3=t+}(DJtOYO!s`jHyo-}t@X|U*9^sIfaZfh;YLqEFmZ^E;$_XK}%eq;>0 zl?+}*kh)5jGA}3daJ*v1knbW0GusR1+_xD`MFPZc3qqYMXd>6*5?%O5pC7UVs!E-` zuMHc6igdeFQ`plm+3HhP)+3I&?5bt|V8;#1epCsKnz0%7m9AyBmz06r90n~9o;K30 z=fo|*`Qq%dG#23bVV9Jar*zRcV~6fat9_w;x-quAwv@BkX0{9e@y0NB(>l3#>82H6 z^US2<`=M@6zX=Pz>kb8Yt4wmeEo%TZ=?h+KP2e3U9?^Nm+OTx5+mVGDvgFee%}~~M zK+uHmj44TVs}!A}0W-A92LWE%2=wIma(>jYx;eVB*%a>^WqC7IVN9{o?iw{e4c=CG zC#i=cRJZ#v3<OhgHFO)Yuf*wx=u8?KJAxfFal#c87qImw{QL+yd!UrcHEm`qaIWJ> zF^9V+7u?W=xCY%2dvV_0dCP%5)SH*Xm|c#rXhwEl*^{Ar{NVoK*H6f5qCSy`+|85e zjGaKqB)p7zKNKI)iWe6A9qkl=rTjs@W1Crh(<w{D@{wF@eAUdA<ecn!45g=nz<F8W zcHpM2OaZmr7hg(j>3G57qdT0w2ig^{*xerzm&U>YY{+fZbkQ<WiW=GrQ9?}ABlM?S z5yX^-T$QGSicUUT_;DBFofFw|X+^sREV>#;^<$JniUifmAuEd^_M(&?sTrd(a*cD! z<RfQp$HKS4nD)BZdWrVduooK{Y#BPyLM^%s#T9QaF#!BDh4*GS0;>F*;`m80MrZ^> zaF{}rDhEFLeH#`~rM`o903FLO?qw#_Wyb5}13|0agjSTVkSI6Uls)xAFZifu@N~PM zQ%o?$k)jbY0u|45WTLAirUg3Zi1E&=G#LnSa89F3t3>R?RPcmkF}EL-R!OF_r1ZN` z?x-uHH+4FEy>KrOD-$KHg3$-Xl{Cf0;UD4*@eb~G{CK<fax(qwwJBZTjQv;(6lwZ1 zN@y8!2Q~?JvR=^bgSD}Zo^iruSXBV}rzy#Y@LME2qAW4Y%O+imN5Xc_W5Fh#DBFe; zwY9`azQ@O1eUnX&7vS!|8z%OWQCo_Wg2|qd_%j<t?-<@AfA>-DXe3xpEEls?SCj^p z$Uix(-j|9f^{z0iUKXcZQen}*`Vhqq$T?^)Ab2i|joV;V-qw5reCqbh(8N)c%!aB< zVs+l#_)*qH_iSZ_32E~}>=wUO$G_~k0h@ch`<gt#cp1U1WgWwHf1zyQewkQH>a6Wa zsk;<)^y=)cPpHt@%~bwLBy;>TNrTf50BAHUOtt#9JRq1ro{w80^sm-~fT>a$QC;<| zZIN%&Uq>8`Js_E((_1sewXz3VlX|-n8XCfScO`eL|H&2|BPZhDn}UAf_6s}|<W$yZ z&kmrV`OAcyEk@5O_d1K`9ztw!LTQ)vi^7AY(b7$AK%X!8_!&bvrhLv@oFO}+TfU4o z!H9q63S!`o3%v<@B2F*Pz76V~n+@=u<2KM_4Yf4Tcil0U)}t=ASxe=Js$o)5^i~?< z5OqmfW6-dnOw9@{Aqq4vD4bN1OnS@+lTfgs?eN(FNn5Q#_veOlFdu3)IK$eB^Uo4t zj?l?=#xmRXU%L-sp<dhXj_~_D*FuOEC>!XpmUr90v|nCutzMjb9|&}#Y7fj_)$alC zM~~D6!dYxhQof{R;-Vp>XCh1AL@d-+)KOI&5uKupy8PryjMhTpCZnSIQ9^Aq+7=Mb zCYCRvm4;H=Q8nZWkiWdGspC_Wvggg|7N`iED~Eap)Th$~wsxc(>(KI>{i#-~Dd8iQ zzonqc9DW1w4a*}k`;rxykUk<ZJ`qoPZH+s1L|{7dJ03F>+~N)|*I?@0901<qh{Z9u zM(%*;?u7Tx@An5HnDFSwh~71l4~zl+IS3QFak$TAn}O;_&Yg6&yC;97-}}S=>R`xy zN{20p@Ls<%`1G1Bx87Vm6Z#CA`QR(x@t8Wc?tpaunyV^A*-9K9@P>hAWW9Ev)E$gb z<(t?T<I%q{eh<paBCgp(eNP1JC7j$cU&lqI%}1$+t<Xum)7-hy-(S~>e6GcJX2&0% z403pe>e)>m-^qlJU^kYIH)AutgOnq!J>FoMXhA-aEx-((7|(*snUyxa+5$wx8FNxS zKuVAVWAr<NYYOV+XC<zEq=BX*l6of(_0jkouf~Z}i)Pi;@oSKe*2S%Ot!8e9G()D^ zHCF=S(f7vqeckT}E9Gkn7-$v6Rolof1?4D(Ee6t+oZ0lsJ=UPx<vWKk)>lK#kDzEM zqR?&aXIdyvxq~wF?iYPho*(h<uGlq#b_^JO#6P~MgKdi{;dc6bOPRw@UTRu@s@>?k zD(SBpRDZ}z$A})*Qh!9&pZZRyNixD!8)B5{SK$PkVET(yd<8kImQ3ILe%jhx8Ga-1 zE}^k+Eo^?c4Y-t2_qXiVwW6i9o2qosBDj%DRPNT*UXI0=D9q{jB*22t4HHcd$T&Xi zT=Vte*Gz2E^qg%b7ev04Z&(;=I4IUtVJkg<`N6i7tjUn-lPE(Y4HPyJKcSjFnEzCH zPO(w%LmJ_=D~}PyfA91H4gCaf-qur3_KK}}>#9A}c5w@N<r?JvNjY~yQShiS4qY&3 zlEq{*4cG8TB8w?hxny#0kg_47TjeF0N4fFfRug<oQH4Q(9JenqW{)rACv`ezyz-yU zXWQaxZzc6w)o5k1X`jL!9euTR%&XzA(yX>;-#cHph=x}^mQ3`oo`Y$ope#)H9(kQK zGyt<7eNPuSAs$S%O>2ElZ{qtDIHJ!_THqTwcc-xfv<@1>IJ;YTv@!g-zDKBKAH<<p zBDDsGt$u2qMC-^a?PmMtEGv5Qjw-8`x+??EVCj)0tD5~cjb`<Ru8=Di2fXP=Xsa4y z&n#+a?$v9OkH1zuW`su>Zet1e^8c}8fE97XH}+lF{qbF<`Y%dU|I!~Y`ZrVfKX82i z)(%!Tcf~eE^%2_`{WBPGPU@1NB5SCXe1sAI<4&n1IwO{&S$ThWn37heGOSW%nW7*L zxh0WK!E7zh%6yF-7%~<m{+EMBci$fO&hv0iZf0iciMJ_<^l~es_{rqv)3kTa)Ak7+ z^Xo_#|0iZI&^uj#ODfeL#OGhjgkcd>l@I~b`2=*$;RYbi(I#zp$gL_d39U4A)KuB( zcS0bt48&%G<QI2DbY;&fyt@4p`kndvOAsyITmfiaVnddQPW><k4f~&M47%t~>_I~( zL(}w&2NA6#$=|g)J+-?ehHflD^lr77ngdz=dszFI;?~ZxeJv=gsm?4$$6#V==H{fa zqO!EkT>1-OQSJoX)cN}XsB;shvrHRwTH(I2^Ah4|rizn!V7T7fLh~Z<`Q+?zEMVxh z$=-x^RR*Pl<N5e(X;~A8VM_P?TZ%aBKgo&=4$TErD)@Yct1Rw?ng{l|AoY=?j%yN0 z{#cO{%|$VQvwftyGPCmDv`G|@hi=(&+FD`aH0@zL)mgk61`d7fWFI<9n5Stfh{y~| zVYivv;t1&zm<!4~89}Fc?b(Kg_9R40b-;<;G;xsNR2o!c=iwxzn4nij;=KC8R)gz3 z9{q)1S1P63>hkV_8mshTvs+zmZWY&Jk{9LX0Nx|<ldHT!kKyn#dbVMfBn9e@+8r+F zfUf&0TK=f&Dw}lCHqy=C!Y_ll#;7`Ni~dQ7*RF-@CT118I8||q-;pR+UUO=*ir<_t z#spc+WCC_&j^sM1My2U+FVEl;KnC$f^WTRS8%6rW@=8`+%Q<P=bTsD{BzbOLv4B=< znii$?HN+aTLVM;6Ry2|w16RXk8F{P;vF6P*>+NAEq-^+Rh|ZlinVZ=e8=`WQt;e@= zPU}^1cG*O;G7l<KDc2~6h#xMeWr-r0OAVri(64~%KI0R2+$-rI{tJE2uRmY>{Y#nl znp`y%CO_SC7gk0i0gY&phM04Y)~vU0!3$V$2T+h(1ZS<f8b%S8rz4-~;5aW>+cCgc zaC?3M;B48^faGo>h~--#FNFauH?0BJJ6_nG5qOlr>k~%DCSJaOfl%KWHusw>tG<g2 z$lo!8f^Xe%pj=Rq7%tJ{i>rTxAhlEVDxc8R2C-)LCt&$Rt9IKor=ml7jirX@?WW+M z^I{b<RO!Q<u)IU5t7<PW#57>}MD5r$s>^^sN@&g`cXD~S_u09xo;{;noKZatIuzqd zW1e7oTl9>g8opPBT(p+&fo0F#!c{NFYYpIZ6u8hOB{F#{nP)@}<EI#MDyucB{#6)L zh?JbpGIyYUsx1TNY%9e(fQxI4t~H%dE@^{WcxhZ!EGpG(z;pkdxe<EMwA+Lw4=;2g zYbi-SoGU)S_pwcYeS^ZA!|qTP6{pVI-|SNsgg%*BWh(Meg~tf-Q>)X20$3iJtG$cO zJ$Oxl_qH{sL5d?=D$2M4C3Ajc;GN0(B-HVT;@pJ-LvIrN%|SY?t}g!J>ufQrR%hoY z!nr$tq~N%)9}^tEip93XW=MQ1@XovSvn`PTqXeT9@_7hGv4%LK1M**Q%UKi|(v@1_ zKGe*@+1%Y4v&`;5vUL`C&{tc+_7HFs7*OtjY8@Gg`C4O&#An{0xOvgNSehTHS~_1V z=daxCMzI5b_ydM5$<?dgyKM^=r)Tc6U|s}2kynE;FGHeu-B988SO;&pB(e6Qh2P=z z3xHw_PzW_~dkx((DUd~Q2N1y~?HHrUe^BBMG0xxXk7M0LA9EBTCq5C@%1ysh#Z!@~ zeBSi(I#rmd%ndI2&VJ}2ohfjS@n({D#%pBmt^KT`Uq^dIUO)MO6sy=Co=$u5L%1ly zKrztx?JF?i3`s2H+UzoBhg0&Z9qMf`%Goy1(HZK-?+u=1^xjw2TbhuR=eMi!$6G>z zZl`a{mM}i@x;=QyaqJY&{Q^R*^1Yzq!dHH~UwCCga+Us~2wk59ArIYtSw9}tEmjbo z5!JA=`=HP*Ae~Z4Pf7sC^A3@Wfa0Ax!8@H_&?WVe*)9B2y!8#nBrP!t1fqhI9jNMd zM_5I)M5z6Ss5t*f$Eh{aH&HBeh3<g7^zLpu^Ry#)H8VHEiRW^liKzzBoM3#P@ytA< zA@5R;`2dqNGoWM#nC%jlTW~eu$^Qc*+dkom?FLAYw(n7mMai@*PO})<Dp$Ok0Hd|J z{nPfV$w6+Nq{4I+p~1*KT9hjW@0B__I&Mskiv;drVlpZ7bg1FkO*IdCid;LJ_4!7K zbfkj~O7n!d8(RlYcP}&ccfRG>10Q~tRl3wCEcZ>WCEq%3tnoHE)eD=)XFQ7NVG5kM zaUtbnq2LQomJSWK)>Zz1GBCIHL#2E>T8INWuN4O$fFOKe$L|msB3yTUlXES68nXRX zP6n*zB+kXqqkpQ3OaMc9GqepmV?Ny!T)R@DLd`|p5ToEvBn(~aZ%+0q&vK1)w4v0* zgW44F2ixZj0!oB~^3k|vni)wBh$F|xQN>~jNf-wFstgiAgB!=lWzM&7&&OYS=C{ce zRJw|)PDQ@3koZfm`RQ$^_hEN$GuTIwoTQID<d@J+C!*a#y8F@xM-Iy_j&S_v$*aHC z<^<1lMFmAQ6d)B9ppuP7+x{7e>b?W&wEo@c75$dW(ER6q)qhF`{#7UTuPH&)w`F!w z0EKs}=33m}_(cIkA2rBWvApydi0HSOgc>6tu&+hmRSB%)s`v_NujJNhKLS3r6hv~- z)Hm@?PU{zd<SuU^ZNqbh_hj?zhJVNRM{0ipOFcz-sswR>0Tga)cJWb2_!!9p3sP%Z zAFT|jy;k>4X)E>4f<s%$es?%H6q44Ym7Tg^bK_WZ>h^6=SxV5w6oo`mus&nWo*gJL zZH{SR!x)V)y=Qc7WEv-x<Rp}|n<G?y@SQ4XooI*D5H6|yT}sqCm#c1ra{^IYypH}c zm17W3XkTgz;cv-2Bkm9zj!KK~b{5nJs-w29PNOBOi7M%$)E08H=v6$}lUmUa(5>LR zhD4OcBwjW5r+}pays`o)i$rcJb2MHLGPmeOm<ly?oC3vz<dWPHJ2q*qSfdfjHs3pG z8wPe2f#fdLSh@|^lKvdXF_&GOvjikbVR#Qzr>t5XJDg@(O3PCbxdDn{6qqb09X44T zh6I|s=lM6Nr#cGaA5-eq*T=LQ6SlRq*`~`b+dVi5^>el1p;#si6}kK}<i{_X0}mow zhl0h@WibK^GtE>>w;1<WXe4=aU)VR4iAjHDbqV1&<YPjvBdJ|}-XxnB?Tstau<Hfq zCRRqz_iBQn`XqE$^y`!_by;iY`BF&pW5CL^OWe?LiOxoGT#Y$s(kmFjDXs&p?eit> z6B1dz{q_;PY{>DBQ+v@1pfXTd5a*^H9U*;qdj@XBF}MoSSQxVXeUpEM5Z0909&<Re zk3I+&OO%J-Z}&=p!z(}*pf~$i%5?5}NgAE2OZE4Z<X!Mwp;tlq>8$pRfR|B(t0<lD zFs$q_Z$Z*zi1c&2E;a}s$0i^wl);}>ox&xl8{8mUNd#(zWONW{oycv$VjP1>q;jU@ z@+8E~fjz*I54OFFaQ{A5jn1w>r;l!NRlI(8q3*%&+tM?lov_G3wB`<}bQ>1=&xUht zmti5VZzV1Cx006Yzt|%Vwid>QPX8Nfa8|sue7^un@C+!3h!?-YK>lSfNIHh|0kL8v zbv_BklQ4HOqje|@Fyxn%IvL$N&?m(KN;%`I$N|muStjSsgG;gP4Smgz$2u(mG;DXP z<GLhq%Frtu7l<`vL?~}D33W@?AQ|QM%-T&P!X7*@ooXAv3j4ICG}mO0p_It|>f~uQ z212x^l6!MW>V@ORUGSFLAAjz3i5zO$=UmD_zhIk2OXUz^LkDLWjla*PW?l;`LLos> z7FB<H#U>vCr)#)XBByDm(=n%{D>BcUq>0GOV9`i-(ZSI;RH1rdrAJ--f0uuAQ4odl z_^$^U_)0BBJwl@6R#&ZtJN+@a(4~@oYF)yG+G#3=)ll8O#Zv3SjV#zSXTW3h9kqn* z@AHL=vf~KMas}6{+u=}QFumr-!c=(BFP_dwvrdehzTyqco)m@xRc=6b#Dy+KD*-Bq zK=y*1VAPJ;d(b?$2cz{CUeG(0`k9_BIuUki@iRS5lp3=1#g)A5??1@|p=LOE|FNd; z-?5MLKd-5>yQ7n__5W^3C!_`hP(o%_E3BKEmo1h=H(7;{6$XRRW6{u+=oQX<((xAJ zNRY`Egtn#B1EBGHLy^eM5y}Jy0h!GAGhb7gZJoZI-9WuSRw)GVQAAcKd4Qm)pH`^3 zq6EI<JY+MFM(eM!0?iX661nT9c-t~th~b`G4v9)PjuBkKR2nRDgO!=Je!Yr0&>M}Q zxZGx%aLnNP1an=;o8p9+U^>_Bi`e23E^X|}MB&IkS+R``plrRzTE%ncmfvEW#AHJ~ znmJ<w+?(s0eKb5NC>`x&ez6<V)q+T?(ZD{dXt<5#hyU$KG!X$+$^9Yvvrs%2XHa28 z9mW3uNXoj}%%{F;7@vhx@XEris%fqkwras~!0d4n)^sr~-v)u>eT21aLnoI`%pYYj zzQ?f^ob&Il;>6Fe>HPhAtTZa*B*!;;foxS%NGYmg!#X%)RBFe-acahHs3nkV61(E= zhekiPp1d@ACtA=cntbjuv+r-Zd`+lwKFdqZuYba_ey`&H<<cYk$0c=kGPn9qVEX_6 zdd&agdUKm^NSclQfBqr<G?7flcPt3|cAET?xcXoI=>Psu;Tzwt;-LQxvv<_D5;ik7 zwETZe`+voUhk%$s2-7Rqfl`Ti_{(fydI(DAHKr<66;rYa6p8AD+NEc@Fd@%m`tiK% z=Mebzrtp=*Q%a}2UdK4J&5#tCN5PX>W=(9rUEXZ8yj<Mqef_Wl-7%VtnZS%Z2oI}3 zt4>Ru+7<Rn6ogv&Yd+l%+cl%5G3&xkOLP84>)mFpKh{6;n%!bI(qA9kfyOtstGtOl zX!@*O0fly*L4k##fsm&V0j9Lj<_vu1)i?!<L;E`x9lME^PJK;H0I38a2~ay-IQtaM zP*qOEwu?>#xTB7@2H&)$Kzt@r(GH=xRZlIimTDd_o(%9xO388LwC#;vQ?7OvRU_s< zDS@6@g}VnvQ+tn(C#sx0`J^T4WvFxYI17;uPs-Ub{R`J-NTdtBGl+Q>e81Z3#tDUr ztnVc*p{o|RNnMYts4pdw=P!uJkF@8~h)oV4dXu5F7-j0AW|=mt!QhP&ZV!!82*c7t zuOm>B*2gFtq;A8ynZ~Ms?!gEi5<{R_8tRN%aGM!saR4LJQ|?9w>Ff_61(+|ol_vL4 z-+N>fushRbkB4(e{{SQ}>6@m}s1L!-#20N&h%srA=L50?W9skMF9NGfQ5wU*+0<@> zLww8%f+E0Rc81H3e_5^DB@Dn~TWYk}3tqhO{7GDY;K7b*WIJ-tXnYM@z4rn(LGi?z z8%$wivs)fC#FiJh?(SbH-1bgdmHw&--rn7zBWe1xAhDdv#IRB@DGy}}zS%M0(F_3_ zLb-pWsdJ@xXE;=tpRAw?yj(Gz=i$;bsh&o2XN%24b6+?_gJ<Kq?WDXDfm(x!QEt~n zRKS&jm1iAmM3}~9QQzG(ufO3+`TI6D9BPg(#U0I6R;fichT{&%oANc!_k+QyVUA0X zJ;y~@dMky&r&t(&yTq9QF`8JqVvCIcJ)sePA7<JG&$d^_3Hci6_0j&Ey^t-_>DBeY zws3PE2u!#Cec>aFMk#ECxDlAs;|M7@LT8)Y4(`M}N6IQ{0YtcA*8e42!n^>`0$LFU zUCq2IR2(L`f++=85M;}~*E($nE&j;p<yY{=u)t50<zfGuPfQVrd32XaZr0TmMx8R* z@*(HUfN5jM$WN2oIfF}JMksU=KGZ1F5M)`z_dNIl$F|R02`>{l%xchiTau*tB9bI= zn~Ygd@<+9DrXxoGPq}@vI1Q3iEfKRleuy*)_$+hg?+GOg<A}r`+}E9+ehEFhD$oVf z7<m>f1r?d@Or42|s|D>XMa;ebr1uiTNUq@heusd6%WwJqyCCv!L*qou9l!B22H$bQ z)<)IA>Yo77S;|`fqBk!_PhLJEQb0wd1Z|`pCF;hol!34iQYtqu3K=<LO71guVa`H& zP~U?liGQ}(w`Ce;)(XleA+f1HnQZeuVKVi3e|?4RrOGyn8>$QxLW7(HFx~v>`vVRr zyqk^B4~!3F8t8Q_D|GLRrAbbQDf??D&Jd|mgw*t1YCd)CM2$76#Cqj1bD*vADwavp zS<`n@gLU4pwCqNPsIfHKl{5}g<GJ0o#1j?jNyIHMj<CvGpYQW1g$p7}ff8O1($ZwA zM5*w6_w!_W(47!a@lfhj-LO=sv{0AgO+p&pD7RH8U0ABe3klJGcA#Ocb>u9t-o+O< z??!fMqMrt$s}02pdBbOScUrc1T*{*-ideR<m2e=AZal*{t}%C93t*O6?ie5So=e1) z%(avX4jGAsQT|{)jC-)iD|Zh3MH`Qb&c4gk`a!C>6(1q4@oC6mxg8v8Y^h^^hfx6| z|Mld6Ax1CuSlmSJmHwdOix?$8emihK#&8&}u8m!#T1+c5u!H)>QW<7&R$eih)xkov zHvvEIJHbkt+2KQ<-bMR;2SY<W%^(e<vyQcTKPTbhPZ1>X?8SI=_<-J!GD5@P2FJ}K z5u82YFotCJF(dUeJFRX_3u8%iIYbRS??A?;iVO?84c}4Du9&jG<#urlZ_Unrcg8dR z!5I3%9F*`qwk#joKG_Q%5_xpU7|jm4h0+l$p;g%Tr>i74#3QnMXdz|1l2MQN$yw|5 zThMw15BxjWf2{KM)XtZ+e<wJY-!H0vjG6iWB)tDV08z-+*6I6c)VKS`B*Sk5{69vn z{5u6TN@?QT1&qSG(CW-s93-GMUJ%qgOA@PD3u_>#N)ihlkxPe=5ymT9>@Ym%_LF}o z1XhCP`3E1A{iVoHA#|O|&5=w;=j*Qf`;{mBAK3={y-YS$`!0UmtrvzHBfR*s{z<0m zW>4C=%N98hZlUhwAl1X`rR)oL0&A`gv5X79??p_==g*n4$$8o5g9V<)F^u7v0Vv^n z1sp8{W@g6eWv2;A31Rhf5j?KJhITYfXWZsl^`7z`C<F;2vYEX$)O-o}#)bE%Mbj#_ zXvXs}1>FtnFrHUWiD?$pwU6|PQjs|7RA0o9ARk^9$f`u3&C|#Z3iYdh<0R`l2`)6+ z6tiDj@xO;Q5PDTYSxsx6n>bj+$JK8IPJ=U5#dIOS-zwyK?+t^V`zChdW|jpZuReE_ z)e~ywgFe!0q|jzsBn&(H*N`%AKpR@qM^|@qFai0};6mG_TvXjJ`;qZ{lGDZHScZk( z>pO+%icp)SaPJUwtIPo1BvGyP8E@~w2y}=^PnFJ$iHod^JH%j1>nXl<3f!nY9K$e` zq-?XYl)K`u*cVXM=`ym{N?z=dHQNR23M8uA-(vsA$6(xn+#B-yY!CB2@`Uz({}}w+ z0sni*39>rMC!Ay|1B@;al%T&xE(wCf+`3w>N)*LxZZZYi{5sqiVWgbNd>W*X?V}C- zjQ4F7e_uC<rrMQOhnlaly82U^Bnjl*Ps^;dHP4)`o{y`Br!oGok57zV%6AfCzrx6b zRtkN#-_l5Q6R888F!*RBowS6c#F3(y>UOHbtewQkq?m$*#@ZvWbu{4i$`aeKM8tc^ zL5!GL8gX}c+qNUtUIcps1S)%Gsx*MQLlQeoZz2y2OQb(A<DL3;)MXXTQ`RBN=2Nqo zm|%J=&6B(G>73Jc3`LmlQf0N{RTt;wa`6h|ljX1V7UugML=W5-STDbeWT<mSwJhXL z!aS2TX&k8S`&e){@?u0)ndhS|I5*P`AXfL2^cmXY+Y4+;A$3^)gf$wPi}{Qvn3?Ry z7vEE&$5<Ru_Q#P8!_=cYOw%AF1OLsyT<5t8ut0pRH0SVIuwRf%vxrV$xV&O$O=zu4 zELRNs*8N_EW5BHpx`+}r&eA)WZcQ>iEMjPQ$({hn_s&NDXz<!=4N<vgMcI^yn~Zh` zwvKP>s6?PLySp$?L`0ilH3vCUO{JS0Dp`z;Ry$6}R@1NdY7rxccbm$+;ApSe=2q!0 z()3$vYN0S$Cs)#-OBs{_2uFf}L4h$;7^2w20=l%5r9ui&pTEgg4U!FoCqyA<B2GjD zdx)l4;&dHHVJdZ^Xw&qfECp24<|xWqw2<&|dxV~DnR~Oku@x1r5LF<ueYl&b5>6r2 zC5s72l}i*9y|KTjDE5gVlYe4I2gGZD)e`Py2gq7cK4at{bT~DSbQQ4Z4sl)kqXbbr zqvXtSqMrDdT2qt-%-HMoqeFEMsv~u)-NJ%Z*ipSJUm$)EJ+we|4*-Mi900K{K|e0; z1_j{X5)a%$+vM7;3j>skgrji92K1*Ip{SfM)=ob^E374JaF!C(cZ$R_E>Wv+?Iy9M z?@`#XDy#=z%3d9&)M=F8Xq5Zif%ldIT#wrlw(D_qOKo4wD(fyDHM5(wm1%7hy6euJ z%Edg!>Egs;ZC6%ktLFtyN0VvxN?*4C=*tOEw`{KQvS7;c514!FP98Nf#d#)+Y-wsl zP3N^-Pnk*{o(3~m=3DX$b76Clu=jMf9E?c^cbUk_h;zMF&EiVz*4I(rFoaHK7#5h0 zW7CQx+xhp}Ev+jw;SQ6P$QHINCxeF8_VX=F3&BWUd(|PVViKJl@-sYiUp@xLS2NuF z8W3JgUSQ&lUp@2E(7MG<OQ<1?G8Oxn1mPIGm|_f4YK>`sh4X!LQFa6;lInWqx}f#Q z4xhgK1%}b(Z*rZn=W{wBOe7YQ@1l|jQ|9ELiXx+}aZ(>{c7Ltv4d>PJf7f+qjR<fc zzR_{hk@QY1I>U8i%XZZFJkj&6D^s;!>`u%OwLa*V5Js9Y$b-mc!t@{C415$K38iVu zP7!{3Ff%i_e!^LzJWhBgQo=j5k<<($$b&%%Xm_f8RFC_(97&nk83KOy@I4k?(k<(6 zthO$3yl&0x!Pz#!79bv^?^85K<UzI_1JfNcJfpb(WrpN_?tYT4KP^sShAp~8Y=Yws zA@JeU`}g*o&VzCDoSv8w<0m@Te#}RYK=_*+uR+WvQh1{$#1D!v7brY3q!8^<WIBmB zlc38GyC2MM5lZ=XHVy=Dh?$PiUm%y}K+T{hTd#Tq;{u8ES9|k;|6DUQQ~dPK|Bj{e z-yh=tI;M(zBiyWP^^N}hb?O}{`wysi@QxX46O{{n0Q3r2R{;O6khWXEYRD>5e7uS$ zJ33yka2VzOGUhQXeD{;?%?NTYmN3{b0|AMtr(@bCx+c=F)&_>PXgAG}4gwi>g82n> zL3DlhdL|*^WTmn;XPo62HhH-e*XIPSTF_h{#u=NY8$B<fbww+h*xf==B0x6v(_G?& z!09&2Mgs&r58WroXO=@73B$sl<)3NA_!ZVqwBIT1>UW=5@PD{P5n~g5XDg?Fzvb_u ziK&CJqod4srfY2T?+4x@)g9%3%*(Q2%YdCA3yM{s=+QD0&IM`8k8N&-6%iIL3kon> z0>p3BUe!lrz&_ZX2FiP%MeuQY-xV<vshB><n!bv2W_v>V%K?=bGPOM&XM0XRd7or< zy}jn_eEzuQ>t2fM9ict#ZNxD7HUycsq76IavfoNl$G1|t*qpUSX;YgpmJrr_8yOJ2 z(AwL;Ugi{gJ29@!G-mD82Z)46T`E+s86Qw|YSPO*OoooraA!8x_jQXYq5vUw!5f_x zubF$}lHjIWxFar8<GeFf9-V5`nyfk8^M5y!M_OoGbS<;@bkn%`fT<BaStsh=v0+@5 zOcC73N9RyOeoa>)tTg8z-FEz)a=xa`xL~^)jIdezZsg4%ePL$^`VN#c!c6`NHQ9QU zkC^<0f|Ksp45+YoX!Sv>+57q}Rwk*2)f{j8`d8Ctz^S~me>RSakEvxUa^Pd~qe#fb zN7rnAQc4u$*Y9p~li!Itp#iU=*D4>d<Ci>vJ{Z~}kqAOBcL8ln3YjR{Sp!O`s=5yM zWRNP#;2K#+?I&?ZSLu)^z-|*$C}=0yi7&~vZE$s``IE^PY|dj^HcWI$9ZRm>3w(u` z-1%;;MJbzHFNd^!Ob!^PLO-xhhj@XrI81Y)x4@<gMtV_Y5Go*HbFejp#(E*>FdsI( za`o4Gy(`T$P?PB?s>o+eIOtuirMykbuAi65Y_UN1(?jTCy@J8Px`%;bcNmPm#Fr!= z5V!YViFJ!FBfEq>nJFk0^RAV1(7w+X<r55RW+Y)^S4T<DuFltq?k*3hd&xYsSj2B& zUGX;nxg;#xjm8VFJ3>`HRgP;nJHJdMa!}&vvduCMoslwHTes_I76|h>;(-9lbfGnt zoZom<C?fEb8E8pWCy|-@u{HxBzv)p1MMq};qNB?SI|@9&P6^gO<;M*Bytc@_K~04{ z;AwbRq5D5P(<L_6N9;<Uu?iTHtN4K;8c}I#KqwaH1qMUHKO}r&^w)OUAS0!WB?-XI zrh7E_KOqY}fSQ15Wq<fRKF}+ChGgSi!dwd$-K{x_m@y;3e?VEQrhW;@$QT-V1=~Rc zBoP7r3KOd#ifEufE=S{`jX+2nWI7w9J4?El&r6%hx-hp!CK|B^D%OJ?TF7K$mo!0< zB3|TLdvs$Z>akOt7<zd8GJ~gO+}ci6N;r4aCNk+Od?kJbIVo(1&oUbk)6HY`TXIq= zqUjdch<xQHvfMhy%lGY0+*M8unTxdt(vP2$mb?<CzZfCG?nUX4KnjU9MrRlaDN3vm zp_4jfRuMx5c+|-5^D1H-X8if1gpxo_C>59AuTX4b$)G8TzJ&m*BV8!vMs9#=e0tWa z%<kVjvU5}5jenPuQ3M}mcKL_0sC!*NdRI6Mjlj77o>)84R=3?tfh72~=Rc;fXwj+x z+25xapYK@2@;}6)@8IL+F6iuJ_B{&A-0=U=U6WMbY>~ykVFp$XkH)f**b>TE5)shN z39E2L@JPCSl!?pkvFeh@6dCv9oE}|{GbbVM!XIgByN#md&tXy@>QscU0#z!I&X4;d z&B&ZA4lbrHJ!x4lCN4KC-)u#gT^cE{Xnhu`0RXVKn|j$vz8m}v^%*cQ{(h%FW8_8a zFM{$PirSI8@#*xg2T){A+EKX(eTC66Fb})w{vg%Vw)hvV-$tttI^V5wvU?a{(G}{G z@ob7Urk1@hDN&C$N!Nio9YrkiUC{5qA`KH*7CriaB;2~2Od>2l=WytBRl#~j`<pdG z4M}tb<uU%2ridMFfC^+i<L~BM1~RL!4p+A^)XrawXV{TA-9EIXauS*Dg}JdVIEw4f z`Ulf7uYtc(vYyEo44G0z5l@5cL?;sbE&RWE2C2qxrkkaRYU_fPr>EYsj}jqK2xD*3 ztEUiPZzEJC??#Tj^?f)=sRXOJ_>5aO(|V#Yqro05p6)F$j5*wYr1zz|T4qz$0K(5! zr`6Pqd+)%a9Xq3aNKrY9843)O56F%=j_Yy_;|w8l&RU1+B4;pP*O_}X8!qD?IMiyT zLXBOOPg<*BZtT4LJ7DfyghK|_*mMP7a1>zS{8>?}#_XXaLoUBAz(Wi>$Q!L;oQ&cL z6O|T6%Dxq3E35$0g5areq9$2+R(911!Z9=wRPq-pju7DnN9LAfOu3%&onnfx^Px5( zT2^sU>Y)88F5#ATiVoS$jzC-M`vY8!{8#9O#3c&{7J1lo-rcNK7rlF0Zt*AKE(WN* z*o?Tv?Sdz<1v6gfCok8MG6Pz<GK)kM#Fa}sldEi&546xI(*0gn=!^c0Tb?>ecx9?C zrQG5j^2{V556Hj=xTiU-seOCr2ni@b<&<!)7uosgxZ*i0qYym72`j<}Tyrcivr8hF zTWq=6QQ);+$xc~E4QH2u0lmUt^J?RB2;UgtoqnRS3b?LRcZe%+5j^7dPEf<r=xdOY zyy(>!j><hqkK&LV11o%uPE<DDKhW(+;>GyHbv!&uBbHjH-U5Ai-UuXx0lcz$D7%=! z&zXD#Jqzro@R=hy8bv>D_CaOdqo6)v<Hr<wD^7>FjZldma5D+R;-)y1NGOFYqEr?h zd_mTwQ@K2veZTxh1aaV4F;YnaWA~|<8$p}-eFHashbWW6Dzj=3L=j-C5Ta`w-=QTw zA*k9!Ua~-?eC{Jc)xa;PzkUJ#$NfGJOfbiV^1au;`_Y8|{eJ(~W9pP9q?gLl5<hv` zq-R>E6|e{xkT@s|Ac;yk01+twk_3nuk|lRu{7-zOjLAGe!)j?g+@-;wC_=NPIhk(W zfEpQrdR<hjW6irILMx?a`MP52iT|l<EuL}y=FO+aN8oz%Xw$R#i}Pd~QvUs-FEq>y z^Q$YBs%>$=So>PAMkrm%yc28YPi%&%=c!<}a=)sVCM51j+x#<2wz?2l&UGHhOv-iu z64x*^E1$55$wZou`E=qjP1MYz0xErcpMiNYM4+Qnb+V4MbM;*7vM_Yp^uXUuf`}-* z_2CnbQ);j5;Rz?7q)@cGmwE^P>4_u9;K|BFlOz_|c^1n~%>!uO#nA?5o4A>XLO{X2 z=8M%*n=IdnXQ}^+`DXRKM;3juVrXdgv79;E=ovQa^?d7wuw~nbu%%l<Xf~?N3{;D$ zdjm^~#KJ}13CHdp-*t*f#IzP~WB3Yc+<O@T)t>sjUugE8HJ9zvZIM^nWvjLc-HKc2 zbj{paA}ub~4N4Vw5oY{wyop9SqPbWRq=i@Tbce`r?6e`?`iOoOF;~pRyJlKcIJf~G z)=BF$B>YF9>qV#dK^Ie#{0X(QPnOuu((_-u?(mxB7c9;LSS-DYJ8Wm4gz1&DPQ8;0 z=Wao(zb1RHXjwbu_Zv<=9n<XR?{HbR^Dll@oqz*Z3oqz|IZQaMx#n2R2moU-^D<z- zga}0seGM5-bTV&hZd771e5gI3t`$^>jK28sS}WssjOL!3-E5>d17Lfnq0V$+IU84N z-4i$~!$V-%Ik;`Z3MOqYZdiZ^3nqqzIjLE+zpfQC+LlomQu-uNCStj%MsH(hsimN# z%l4vpJBs_2t7C)x@6*-k_2v0FOk<1nIRO3F{<KiOBUP%D=G#h*?adbA>E?2DnS}w> z#%9Oa{`RB5FL5pKLkg59#x~)&I7GzfhiVC@LVFSmxZuiRUPVW*&2ToCGST0K`kRK) z02#c8W{o)w1|*YmjGSUO?`}ukX*rHIqGtFH#!5d1Jd}&%4Kc~Vz`S7_M;wtM|6PgI zNb-Dy-GI%dr3G3J?_yBX#NevuYzZgzZ!vN>$-aWOGXqX!3qzCIOzvA5PLC6GLIo|8 zQP^c)?NS29hPmk5WEP>cHV!6>u-2rR!tit<H6K<`F|-L2nvu=hj?^+`eij=B<V}b@ z@B)puoO3cGGxU^niF+;tL-h54X~zdAd5S??I#`w|&&6~3d&$7VkMDU-6b_LMwminU z$6hC<ZypQN)Rld1_YatN&gKL*aM%5O&gsK9^UqsYJ)vc9izs}?3Oc+6fuC6t9H`OC zokZOqyS@s3%8l{A-KTu#<)|R8KfY`!NKd>#F6`_;%4{q^6){_CHGhvAs=1X8Fok+l zt&mk>{4ARXVvE-{^tCO?inl{)o}8(48az1o=+Y^r*AIe%0|{D_5_e>nUu`S%zR6|1 zu0$ov7c`pQEKr0sIIdm7hm{4K_s0V%M-_Mh;^A0*=$V9G1&lzvN9(98PEo=Zh$`Vj zXh?fZ;9$d!6sJRSjTkOhb7@jgSV^2MOgU^s2Z|w*e*@;4h?A8?;v8JaLPCoKP_1l- z=Jp0PYDf(d2Z`;O7mb6(_X_~z0O2yq?H`^c=h|8%gfywg#}wIyv&_uW{-e8e)YmGR zI0NNSDoJWa%0ztGzkwl>IYW*DesPRY?oH+ow^(>(47XUm^F`fAa0B~ja-ae$e>4-A z64lb<us@kdtAYl$q}T24sw~n@T~wTnN38G!o-w}D+ML3`i~B`pnM`W>_;|W0ppKI+ zxu2VLZzv4?Mr~mi?WlS-1L4a^5k+qb5#C)ktAYGUE1H?Vbg9qsRDHAvwJUN=w~AuT zUXYioFg2Dx-W)}w9VdFK#vpjoSc!WcvRZ_;TgHu;LSY*i7K_>Px{%C4-IL?6q?Qa_ zL7l=EEo|@X&$gX;fYP02qJF~LN9?E-OL2G(Fo4hW)G{`q<UNTVyu{YECrRdQW8>nW zTIuc+-1VJvKgph0jAc(LzM);Pg$MPln?U|ek{_5nNJHfm-Y#ec+n#Yf_e>XfbL<Jj zC4<j?s_P+<9*S#zb-*>bN)eqHEDr0#?<;TskL5-0JGv|Ut{=$Xk8hlwbaMXdcI3GL zY-hykR{zX9liy$Z2F3!z346<C_U+V9&~+9_ThfF;_W=t2C&Z*UOnbsL(`lg7Y_9mJ z;x7x7msWl4Kb@@$yKgTE5^PM^6EXwa%=X!zvj`?R^UpwmF%I*&db9Mf*}H~d_$T0q zJoI|73QSz<E7i=;AOnv*#a{snA^{$tEWm9D%Wo|FR=1KqgS+BG;5mCU#nURc7oq_o z-O{0O`-W6(TF8B|;h9i-$1&@yllU>uu%9@-y6Gda`X2*ixlD_P@<}K?AoV?(%lM%* z(xNk=|A()443aGj)-~I<t=+b5+qP|cw{6?DZQHi(?%l@p+<VT%oIB@CM6Fs;Kk7%t z%J?!X^U3#ByqT%i5eJsK{B+>Df3J+UA2p2lh6ei^pG*HL#SiThnIr5WZDXebI)F7X zGmP-3bH$i$+(IwqgbM7h%G5oJ@4{Z~qZ#Zs*k7eXJIqg;@0kAGV|b=F#hZs)2BYu1 zr8sj#Zd+Iu^G}|@-dR5S*U-;DqzkX3V0@q-k8&VHW?h0b0?tJ-Atqmg^J8iF7DP6k z)W{g?5~F*$5x?6W)3YKcrNu8%%(DglnzMx5rsU{#AD+WPpRBf``*<8F-x75D$$13U zcaNXYC0|;r&(F@!+E=%+;bFKwKAB$?6R%E_QG5Yn5xX#h+zeI-=mdXD5+D+lEuM`M ze+*G!zX^xbnA?~LnPI=D2`825Ax8rM()i*{G0gcV5MATV?<7mh+HDA7-f6nc@95st zz<x3S-=O9@1Qx`EDk(L<enRy4$&H~91Dqvi*j`&df5YvnJ92?*;!1D{y*{vSKT#)! z`8&J6_mr>C_si$<QVr`<>{|&=$MUj@n<ZkLuF(toIVKp(6>Lxl_HwEXb2PDH+V?vg zA^DJ<z&3Iv0y>%dn069O9<Ouc(<|V99`h3|>TNK-jV}cQKh|$L4&Uh`?(z$}#d+{X zm&=KTJ$+KvLZv-1GaHJm{>v=zXW%NSDr8$0kSQx(DQ)6<U)@wRatQ0n^IU+=Y(tsk z>S?%sWSHUazXSEg_g3agt2@0nyD?A?B%9NYr(~CYX^&U#B4XwCg{%YMYo<flw!Uv7 zbJrd*bK4--;t<&j37ZT@jUbZ8-Qk8uL-t5+XilHP`7ykYb{?`@R8n-Wi%nqiF#0hx zPg@t)?pcqM%L}PMzv3OTb>%e68HVJ7`9KR`mE*Wl7&5t71*R3F>*&hVIaZXaI;<mI z|Ap3H0(aXS@X(VR*Ol`mi%np^ZEHYHRc@ElhxGOh`)3v}+0ls>2a$?;{Ew{e3Hr1* zbf$&Fyhnrq7^hNC+0#%}n^U2{ma&eS)7cWH$bA@)m59rXlh96piJu@lcKl<>+!1#s zW#6L5Ov%lS(?d66-(n`A%UuiIqs|J|Ulq0RYq-m&RR0>wfA1?<34tI?MBI#a8lY{m z{F2m|A@=`DpZpwdIH#4)9$#H3zr4kn2OX!UE=r8FEUFAwq6VB?DJ8h59z$GXud$#+ zjneIq8uSi&rnG0IR8}UEn5OcZC?@-;$&Ry9hG{-1ta`8aAcOe1|82R7EH`$Qd3sf* zbrOk@G%H7R`j;hOosRVIP_2_-TuyB@rdj?(+k-qQwnhV3niH+CMl>ELX(;X3VzZVJ ztRais0C^L*lmaE(nmhvep+peCqr!#|F?iVagZcL>NKvMS_=*Yl%*OASDl3(mMOY9! z=_J$@nWpA-@><43m4olSQV8(PwhsO@+7#qs@0*1fDj70^UfQ(ORV0N?H{ceLX4<43 zEn)3CGoF&b{t2hbIz;Og+$+WiGf+x5mdWASEWIA*HQ9K9a?-Pf9f1gO6LanVTls)t z^f6_SD|>2Kx8mdQuiJwc_SmZOZP|wD7(_ti#0u=io|w~gq*Odv>@8JBblRCzMKK_4 zM-uO0Ud9>VD>J;zZzueo#+jbS7k#?W%`AF1@ZPI&q%}beZ|ThISf-ly)}HsCS~b^g zktgqOZ@~}1h&x50UQD~!xsW-$K~whDQNntLW=$oZDClUJeSr2$r3}94Wk1>co3beS zoY-7t{rGv|6T?5PNk<Z}${YyAJWnFYd_(8lLGvKygk2|9Q-+MgjJ$&KDpf_$YQ?IV zR<<Gym6HGU;;bqndvCX&FnDKQ=}UsHCpxg@6}a(-T<EY&D8er_EV=18JTgdg;NT>Y zj*XjF()ybvnVz5=BFnLO=+1*jG>E7F%&vm6up*QgyNcJJPD|pHoZ!H6?o3Eig0>-! zt^i-H@bJ;^!$6ZSH}@quF#RO)j>7A5kq4e+7gK=@g;POXcGV28Zv$jybL1J`g@wC# z_DW1ck}3+n@h2LFQhwVfaV@D+-kff4cel<IcrWN-M5x8!Ow)bPrn9?d=kx(pB}Zxh zwSayS{c`WwwOA@rCTI0Jpf!LQ0BRAS&Yy^!S}_9)?rVFlb`0@yQL-u&w?3z@i}YtX z&orQmrCH2ERpv_}L+8*5x0r*ar=W0%g{;gnuf;Y%mP^vf>ZC0;0e<L_F@Y}Mun9fT z3*0k%P9JzWMDIiaJzHp78U80rEHg<Jm$kJ?b#g(IM#`$0x_Y_c_XAFK5m}j&*?B9q zSa0I1M-ZM%K;M9EzJ}%_K>f?pA#*PPd8Kk8sO1wza&BHQFblVU8P1=-qScHff^^fR zycH!hlHQs7iejITpc4UaBxzqTJ}Z#^lk{W(cr`qtW~Ap;HvuUf#MxgEG?tEU+B?G% znu<!7LIgR13M|s?%o25M!Ve^n&=M7iB|RnrBtHAJ6<h+az+`2J^UgIdUBonl2DJ}4 zu`>b0I(s@XvI(lva}$Z7<}Qg=rWd5n)}rX{nb+Aw;}?l9LZI-`N-*hts=c6XgjfJs ztp>-686v6ug{glEZ}K=jVG|N1WSWrU*&ue|4Q|O@;s0#L5P*U%Vx;)w7S0ZmLuvwA z@zs2Kut)n1K7qaywO#TbBR`Q~%mdr`V)D`|gN0!07C1!r3{+!PYf9*;h?;dE@#z(k z;o`g~<>P|Sy$ldHTUR3v=_X0Iw6F>3GllrFXVW?gU0q6|ocjd!glA)#f<BmJPFLB} zEhYST*M)esm5(_%C4PWZ`=77E`8iyIH2-_uviC}ybZBAkkU&oTXd<qb;^^X8)}WK^ zZ7VNp$iQ33bjEa{enF`vr_fcnpn5o$xWG}@)wW01agAanwm7U-_6$&kb?+oC`!H4+ z&pP-ziAbnW{HLL*!kOtg5&^#>0G7i20ly>qxRljgfO2)RVpvmg#BSrN)GbGsrIb}9 z1t+r;Q>?MGLk#LI5*vR*C8?<QWz^KoEAbUtRx5!VLSb(M>McB|=AoAjuDk&Pn`KQo z`!|mi{Cz@BGJ!TwMUUTkKXKNtS#OVNxfFI_Gfq3Kpw0`2AsJv9PZPq9x?~kNNR9BR zw#2jp%;FJNoOzW<aW@Re3s=7#KmRWefd}w)30vR+&FhD2(gU`Fzb()i9D)B9j6NR7 zkJkCe-V+Ma{GvGf>>tE#zskPICp>XSs?|B0E%DaJH)rtLA}$Y>?P+vEOvr#8=pylh zch;H3J`RE1{97O+1(1msdshZx$it^VfM$`-Gw>%NN`K|Tr$0}U`J?EBgR%bg=;et0 z_en)!x`~3so^V9-jffh3G*8Iy6sUq=uFq%=OkYvHaL~#3jHtr4sGM?&<HYL8mdfSx ztkF3uXPD7B%V!)xiIi#%hUfzhNcr^0s8kh=m867SDXDO+xe{k-jp8#%R!yLQpP$4P zf+D;?r|{x)(t_iuhw-Sf9iN(g5)W$qGm7jNa&s+!+UzY%8B+JZx+Aosvv8kXrU6rb zbQ18o1Dg{bl=D8~XI)Q-KVuC}csZdF-ol*J*r7G~M0*vV{!wbJm+#70TdwI4^jg?I z%o(r?JZMS5y2Jci`m?!x+iXdwln`R~M+kHX0;phyD<h&PZ%FP7M8{whE<vaSf=2n@ zL*m{)inJF%@r0tqzHPZthaV66%Yd~6StFWr<`uzSKz^t?FA@TuzVR~p6~1ziob2qD zQ%Zy{Gz{hEqc|tEc0|+7<RW>uY&U8N1G}QTMdqBM)#oLTLdKYOdOY%{5#Tgy$7QA! zWQmP!Wny$3YEm#Lt8TA^CUlTa{Cpp=x<{9W$A9fyKD0ApHfl__Dz4!HVVt(kseNzV z5Fb`|7Mo>YDTJ>g;7_MOpRi?kl>n(ydAf7~`Y6wBVEaxqK;l;}6x8(SD7}Tdhe2SR zncsdn&`eI}u}@^~_9(0^r!^wuKTKbs-MYjXy#-_#?F=@T*vUG@p4X+l^SgwF>TM}d zr2Ree{TP5x@ZtVcWd3++o|1`BCFK(ja-QP?zj6=ZOq)xf$CfSv{v;jCcNt4{r8f+m zz#dP|-~weHla%rsyYhB_&LHkwuj83RuCO0p;wyXsxW5o6{)zFAC~2%&NL?<TC?7g@ zfqoa;enQ6=kuI+FtDKTp*4K87i40xomn^i4?-U687)dVCvUn@i5Um!YDhz&=8zf3a z*UH64F1?04tzG*#1=sim1h4x8=I0_~0BivP+v+Lk^FOu&1AE%&=MCtDidMqo6t?0> z=mA}szjHKsVSSnH#hM|C%;r0D$7)T`HQ1K5vZGOyUbgXjxD%4xbs$DAEz)-;iO?3& zXcyU*Z8zm?pP}w&9ot_5I;x#jIn^Joi5jBDOBP1)+p@G1U)pL6;SIO>Nhw?9St2UN zMedM(m(T6bNcPPD`%|9dvXAB&IS=W4?*7-tqldqALH=*UapL!4`2TM_{`W&pm*{?| z0DcsaTdGA%RN={Ikvaa&6p=Ux5ycM){F1OgOh(^Yk-T}a5zHH|=%Jk)S^vv9dY~`x zG+!=lsDjp!<Zw<>D}7o94RSQ-o_g#^CnBJlJ@?saH&+j0P+o=eKqrIApyR7ttQu*0 z1f;xPyH2--)F9uP2#Mw}OQhOFqXF#)W#BAxGP8?an<=JBiokg;21gKG_G8X!&Hv;7 zP<bTe@P=slWtf9t{y!Y^e<ETc?nc%wPQRvkq88RB0!Bu^b6pt&TLZKI9P1{lZ8~AA zVgBH1ENoP|cw1DcPRqz@QgYQNgGokM3*xNG9!q77#Av0)In!jXVb{72TcVC`DP;(1 zk+-(Y$?Lo4!^1FLOIH%Rhdh-}(GOz7`~{5l*$>9Vpzm#@;^-lf=6POs>UrGm-F>-! zm;3qp!Uw?VuXW~*Fw@LC)M%cvbe9!F(Oa^Y6~mb=8%$lg=?a0KcGtC$5y?`L5}*-j z7KcU8WT<U{=H%2rUviZgG-R^Il^D(umJq{>>2PpKx<58`m((l9^aYa3uP{PMb)nvu zgt;ia9=ZofxkrW7TfSrQf4(2juZRBgcE1m;WF{v1Fbm}zqsK^>sj=yN(x}v9#_{+C zR4r7abT2cS%Wz$RVt!wp;9U7FEW&>T>YAjpIm6ZSM4Q<{Gy+aN`Vb2_#Q5g@62<R4 zMx$6~v*mbHZfPOwxp<OAlg!hqzrj>uR_>II@eiHaay+JU$J=#>DY9jX*2A=&y8G%b zIY6gcJ@q)uWU^mSK$Q}?#Arq;HfChnkAOZ6^002J>fjPyPGz^D5p<P8nMaP(*LAGP z#-zU2OJ^z3Db=`NZQ>}o;h2VLNTI{HGg!obo3K!*I~a7)p-2Z3hCV_hnY?|6i`29b zoszLpkmch$mJeupLbt4_u-<3k;VivU+ww)a^ekoIRj4IW4S<FRqdy{2RiwFY> z{z%4_dfc&HAtm(o`d{CZ^AAIE5XCMvwQSlkzx3cLi?`4q8;iFTzuBAddTSWjfcZp* zn{@Am!pl&fv#k|kj86e$2%NK1G4kU=E~z9L^`@%2<%Dx%1TKk_hb-K>tq8A9bCDfW z@;Dc3KqLafkhN6414^46Hl8Tcv1+$q_sYjj%oHz)bsoGLEY1)ia5p=#eii(5AM|TW zA8=;pt?+U~<O0(jQ4OX$<Sydbm#~h&)W7v$5#U`FsQ0@Df3>>`|J(B85BKE0cB4n> zWrgZ)Rbu}^A=_oz65LfebZ(1xMjcj_g~eeoj74-Ex@v-q9`Q{J;M!mITVEfk6cn!u zn;Mj8C&3^8Kn%<`Di^~Y%Z$0pb`Q3TA}$TiOnRd`P1XM=>5)JN9tyf4O_z}-cN|i> zwpp9g`n%~CEa!;)nW@WUkF&<|wcWqfL35A}<`YRxV~$IpHnPQs2?+Fg3)wOHqqAA* zPv<6F6s)c^o%@YqS%P{tB%(Lxm`hsKv-Hb}MM3=U|HFgh8R-|-K(3m(eU$L@sg=uW zB$vAK`@>E`iM_rSo;Cr*?&wss@UXi19B9*0m3t3q^<)>L%4j(F85Ql$i^;{3UIP0c z*BFId*_mb>SC)d#(WM1%I}YiKoleKqQs<A5DyhV`a20Ec$*bh4vW6b6#9lSmf~?r* zlcL&gHfFhvg{m>wkdhRt9%_dAnDaKM4IEJ|QK&BnQ@D;i-ame%MR5XbAfE0K1pcxt z{B5_&OhL2cx9@Sso@u2T56tE0KC`f4IXd_R3ymMZ%-!e^d}v`J?XC{nv1mAbaNJX| zXau+s`-`vAuf+&yi2bsd5%xdqyi&9o;h&fcO+W|XsKRFOD+pQw-p^pnwwYGu=hF7& z{cZj$O5I)4B1-dEuG*tU7wgYxNEhqAxH?p4Y1Naiu8Lt>FD%AxJ811`W5bveUp%*e z9H+S}!nLI;j$<*Dn~I*_H`zM^j;!rYf!Xf#X;UJW<0gic?y>NoFw}lBB6f#rl%t?k zm~}eCw{NR_%aosL*t$bmlf$u|U2hJ*_rTcTwgoi_N=wDhpimYnf5j!bj0lQ*Go`F& z6Wg+xRv55a(|?sCjOIshTEgM}2`dN-yV>)W<s8ZX^F)rd_eolw0O4mBB)~DVnQ5dX zh1MfhOJ9Pzd<LR=!m@e-i*a1>f$J58>lNVhjRagGZw?U9#2p!B5C3~Nc%S>p`H4PK z7vX@|Uo^*F4GXiFnMf4gwHB;Uk8X4TaLX4A>B&L?mw4&`XBnLCBrK2FYJLrA{*))0 z$*~X?2^Q0KS?Yp##T#ohH1B)y4P+rR7Ut^7(kCwS8QqgjP!aJ89dbv^XBbLhTO|=A z|3FNkH1{2Nh*j{p-58N=KA#6ZS}Ir&QWV0CU)a~{P%yhd-!ehF&~gkMh&Slo9gAT+ zM_&3ms;1Um8Uy0S|0r{{8xCB&Tg{@xotF!nU=YOpug~QlZRKR{DHGDuk(l{)d$1VD zj)3zgPeP%wb@6%$zYbD;Uhvy4(D|u{Q_R=fC+9z#sJ|I<$&j$|kkJiY?AY$ik9_|% z?Z;gOQG5I%{2{-*)Bk|Tia8n>TbrmjnK+8u*_cS%*;%>R|K|?urtIdgTM{&}Yn1;| zk`xq*Bn5HP5a`ANv`B$IKaqA4e-XC`sRn3Z{h!hN0=?x(kTP+fE1}-<3eL+QDFXN- z1JmcDt0|7lZN8sh^=$e;P*8;^33pN>?S7C0BqS)ow4{6ODm~%3018M6P^b~(Gos!k z2AYScAdQf36C)D`w&p}V89Lh1s88Dw@zd27Rv0iE7k#|U4jWDqo<pw`rT0F1=giby zSvwo-^K5P3?J)*t>UP;-He5cd4V7Ql)4S+t>u9W;R-8#aee-Ct1{fPD+jv&zV(L&k z)!65@R->DB?K6Aml57?psj5r;%w9Vc3?zzGs&kTA>J9CmtMp^Wm#1a@cCG!L46h-j z8ZUL4#HSfW;2DHyGD|cXHNARk*{ql-J2W`9DMxzI0V*($9{tr|O3c;^)V4jwp^RvW z2wzIi`B8cYISb;V5lK}@xtm3NB;88)Kn}2fCH(WRH1l@3<q>XaO7{R*Lc7<o&*hfu zA~y`eH5--g@QhTK;~V;@kFVlBwXL?-xOV}&0LvXLf@G+<_zX>{ZN1m+#&diI7_qzE z?BS+v<)xVMwt{IJ4yS2Q4(77II<>kqm$Jc3yWL42^gG6^Idg+y3)q$-(m2>E49-fV zyvsCzJ5EM4hyz1r#cOh5vgrzNGCBS}(Bupe`v6z{e<CcS{QzMUWAq_nFEe{Vru{6c z|KZrQ|J#+PLzqygyi=3m4BdhVKj0!NsG<U+fK<RKGUFER2&IV8$0<|`B#}lU^@ar> z)cP*a8VCbRuhPp%BUwIRvj-$`3vrbp;V3<u<D|$cxCAE}!0I%pPCYQ!e>wmAUt{?F z0OO?Mw`AS?y@>w%(pBO=0lohnxFWx`>Hs}V$j{XI2?}Btl<q&n{>vIl7!ZMZukDF7 z^6Rq2H*36KHxJ1xWm5uTy@%7;N0+|<>Up>MmxKhb;WbH1+=S94nOS-qN(IKDIw-yr zi`Ll^h%+%k`Yw?o3Z|ObJWtfO|AvPOc96m5AIw;4;USG|6jQKr#QP}+BLy*5%pnG2 zyN@VMHkD`(66oJ!GvsiA`UP;0kTmUST4|P>jTRfbf&Wii8~a`wMwVZoJ@waA{(t(V zwoc9l*4F>YUM8!aE1{?%{P4IM=;NUF|8YkmG0^Y_jTJtKClDV3D3~P7NSm7BO^r7& zWn!YrNc-ryEvh<l>N$$!P%l$Y_P$s8E>cdAe3=@!Igo^0diL6`y}enr`+mQD;RC?w zb8}gXT!aC`%rdxx2_!`Qps&&w4i0F95>;6;NQ-ys;?j#Gt~HXzG^6j=Pv{3l1x{0( z4~&GNUEbH=9_^f@%o&BADqxb54EAq=8rKA~4~A!iDp9%eFHeA1L!Bb8Lz#kF(p#)X zn`CglEJ(+tr=h4bIIHlLkxP>exGw~{Oe3@L^zA)|Vx~2yNuPKtF^cV6X^5lw8hU*b zK-w6x4l&YWVB%0S<MnSL9Gxa+tjTFHHk?^*)Ho+49c->mN<Omsv{<w{M_SX6FrRz& z-fl>{O|!`Sh6H45!7}oYPOc+a#a|n3f%G@eO)N>W!C|!FNXV3taFdpEK*A1TFGcRK zV$>xN<sb#LnQM_qFZRkIc7CDsZFN=(Q&<qDsEKW^u8J}ZvG!S9$V=Gpzacv2#nfBS znUI`V(%8<9w_O9dOzg3pg1KA|xV$L844HD=$^jD7e@tLXu{A?7Q&KD5PmJj(O0Rd} zJ53P3?S>%??ii7jx5D69O>W6O`$M)iQU7o!TPG*+>v6{TWI@p)Yg$;8+W<RxFU`e7 z{bfN`O;EWn(uTD$pTCdDU6G$G0Aqu7uvVLoob|0ph2_mnTUUK%nSix9lQosDs+mxO zQ)7`f=;AM4%2c=yc9`uhF*w;)zK;r4%XrPwRkIJ<^=paRRlSD`dwakGdwU2Bif{P} zfp7I1)Xq0-2F1I22il{2mmE@iA01-nprr3LANk0!$!7K|%&<;M;U1N}-LBaypIar} z*;k|TNIUoLrz6<fTjssa=J@&jpe!_)+(GwYVGQx4+*O=>yE<VTJM=nHJuCiK`4nKF zMjirx-t2fH2j+4NIlyJp!aruMd-O#Tg;Fk{xd%A`<awAfI*L)`XoGXH5K#itZ42AK z6MeknJlNNkn9oZo$LQFbqvB&R31geSNKB|Eazxv7`mmBaie>9DVBMB=vnONSQ6k1v z;u&C4wZ_C`J-M0MV&MpOHuVWbq)2LZGR0&@A!4fZwTM^i;GaN?xA%0)q*g(F0PIB( zwGrCC#}vtILC_irDXI5{vuVO-(`&lf2Q4MvmXuU8G0+oVvzZp0Y)zf}Co0D+mUEZz z<V<U=H+idKcZP;R9F0*dBIp}a_hqpooWwb4eC!W`xqypzPrNaJ>gwR+5y!d(V>s1} zji+mrd_6KG;$@Le2Ic&am6O+Rk1+QS?urB4$FQNyg2%9t%!*S5Ts{8j*&(H1+W;0~ z$frd%jJjlV;>bXD7!a-&!n52H^6Yp}2h3&v=}xyi>EXXZDtOIq@@&ljEJG{D`7Bjr zaibxip6B6M<AvX7F;}xji!{#20`v^r=IX+S_8&y7yMi<{TDCs{)lIgOhlB@q8PxV_ z^K_bV6}m&uNF?(jS7SzI3UW;N4K*THM7W(~LZca^z+Y~4W)ZN|d2h1>f3t#-*Tn7p z96y<T2y#Xcz~YB6wfpE5F$BO)&z2<@Hkm?h8Dj7m{B!BU^}>x1Qv<Gs5lPx{*#im% z@NUr_Fb3h-MOjdYw^i7AWS^$PJ|m%_P(XS98V&Mc6vKJ|E&RDN_MtQRDyP2`@M)J_ zzURj4(W!UW9FwQ-s0z`y>-&r3)4vg`)V~f8>>1_?E4&$bR~uR;$Nz=@U(-vyap|Jx zZ;6Ed+b#GXN+gN@ICTHx{=c@J|97TIPWs(_kjEIwZFHfc!rl8Ep-ZALBEZEr3^R-( z7ER1YXOg<RslpM>Z)&_=`WeHfWsWyzzF&a;AwTqzg~m1lOEJ0Su=C2<{pjK;{d#;E zr2~LgXN?ol2ua5Y*1)`(be0tpiFpKbRG+IK(`N?mIgdd9&e6vxzqxzaa`e7zKa3D_ zHi+c1<wCe5g7HXHML9sFeaTRzfx@YksC+U;4SZXG{&Uk|wK=e(Qcf1Yk{X&1fvGA* zw!EmqXRcWfc`4MVMT4jgS-d7w$hncxD<L9U8AGPq{DMW~K8Ri8c)Yn){n!`p;i$07 z#ata~vsn^kQ0&|_C{SUB&y|DBV~}>`|720|dn(z4Qo<?r+YfX=WYLIOGZslL+F?F4 zhi!IVb|o{L*e^>s^e7sn(PU%NYLv$&!|4kEse%DK;YAD06@XO3!EpKpz!^*?(?-Ip zC_Zlb(-_as+-D?0Ag9`|4?)bN)5o(J=&udAY|YgV(YuK9k=E>0z`$dSaL(wmxd!1f zME&3wwv@#{dgeMlZ4}GL!I`VZxtdQY$lmauCN_|mGXqEEj@i~du$|>5UvLjsbq!{; z@jEf;21iC1jFEmIPE^4gykHQzCMLj=2Ek4&Fvlpq<v&aTHa%PcF6hP3gHi&X2pI7? zRs|zI%My|qVvab#$}>TlS(0YT%*W<<E1qCRKj`*+qHfroZIGFt`*g(JJYczaOq1<p zKFt!ad?rQ1?xU$hd#Daf#$8YO%FRa8%7V3$gbumUdk9LKdg819bwG6c2wOBm-sRf3 zk9p-%EDe8@<aTLV-!^p3VBa}Sh*-o>>XgH$4ww`D`aihBGkPM(&EG};Cl&wzg8!jL z`rkqPzvH(0Kd{2n=?Bt8aAU&0IyiA+V-qnXVId^qG!SWZ7<H3`F5<$(bO%$Qp=Ouz z0`uw>%_f&i!D{R#7Jo$%tICxY%j)ebORE>3H_c|to}c#HX;HAC?~B;2mmQrMp2;8T zmzde!k7BYg^Z1r|DUvSD3@{6<?xk@V&RPeA-iM-8ZEsb)j#bG;>S<1kndb%Qt%GA# z+sB2&F5L`R&fLRdAlp<CTu!?rj!fsBt75|)qNds8l0~UU_sTAt#1ro9U9#V@t%v{g zS~p`@1`lqmQ7Xe0{$&iA%Cw=}sW$W@D1buwqZm@sDSrn29Opri1>U_pVsJsYDEz{^ zKGaAz#%W+MP<N-Fi>GT+D$+xowMY0=ipM)0p?zym&Aoi)qL(pO_weO(k?s|ELHl^W zviJiFUXRL&?`;3_;mvc02A@sbsW9}#{anvGafZ#ST;}za?XS3}ZG3B4m(SW{>w}Fh z)T5Yi*``Tstmi9SHXmuWSND@cj}qtY!`<ld8zkNC^o#qeE@rzNMw=d~@4{g2!$avC zQ^P%PHs572uWdpsxbgC-@j)P-ulQ-Gi|^22tfzZ#6yDtez%L9#=kCGySK)N@h~uhQ z0B`;+FV!{t9e(^#YQcK>tuD29Dpu+-D3$h<5FY>jE>YJvqBmhw?oll`x7Ono(}R~P zle_eBwYy0Rr7kmf_SEt_gn4)AO-r`}^Z5Y%Rm8)K-?X>rvDL+QT?#)QwDsQ2c$tc* z&#hbgkL6}GnBDH;+lREM6MGIskRa@r>5Iq(ll2IepuhW86w@14=E{<t<+{6ok<;kN z^T~21D{HM?r@qkFNVBvE4LX=Bh^3&vy`GF15gN?PGDEag7(}<dp%VeKx#ugmwCCu? zJ2V=NPDtxBDT2j?{(&iY)^Pt3oXGq86vkpxig;CR2_4!QWI79%k-zy;)N)gqK-|A4 zVb>6$cz*cBDQ)CT>}v-DLM-v8)xaPBnmGBKM63RgDGqh!<*j90tSE4|G^+r@#-7g2 zs8KE8eZPZhQuN>wBU%8CmkE9LH1%O;-*ty0&K~01>F3XB>6sAm*m3535)9T&Fz}A4 zwGjZYVea@Fesd=Rv?ROE#q=}yfvQEP8*4zoEw4@^Qvw54utUfaR1T6gLmq?c9sON> z>Np6|0hdP_VURy81;`8{ZYS)EpU9-3;huFq)N3r{yP1ZBCHH7=b?Ig6OFK~%!GwtQ z3`RLKe8O&%^V`x=J4%^Oqg4ZN9rW`UQN^rslcr_Utzd-@u-Sm{rphS-y}{k41)Y4E zfzu}IC=J0JmRCV6a3E38nWl1G495grsDDc^H0Fn%^E0FZ=CSHB4iG<6jW1dY`2gUr zF>nB!y@2%rouAUe9m0VQIg$KtA~k^(f{C*Af_tOl=>vz>$>7qh+fPrSD0YVUnTt)? z;@1E0a*#AT{?oUs#bol@SPm0U5g<`AEF^=b-~&4Er)MsNnPsLb^;fL2kwp|$dwiE3 zNc5VDOQ%Q8j*d5vY##)PGXx51s8`0}2_X9u&r(k?s7|AgtW0LYbtlh!KJ;C9QZuz< zq>??uxAI1YP|JpN$+{X=97Cdu^mkwlB={`aUp+Uyu1P139=t%pSVKo7ZGi_v(0z>l zHLGxV%0w&#xvev)KCQ{7GC$nc3H?1VOsYGgjTK;Px(;o0`ler<o<VsrVl1L=1LKM* zSr?}pX@JohF$RvbE)o+XPI{gtXbe>xB<+EJX9G9f8b+)VJdm(Ia)xjD&5ZL45Np?9 zB%oU;z05XN7zt{Q!#R~gcV^5~Y^gn+Lbad7C{UDX2Nznj8e{)TLH|zEc|{a#idm@z z6(zon+{a>FopmQsCXIs*4-<r1S$vw!O=S8eXuWVM4gE|O22Aim2fuC!E;^(N17hT} z{W>dLGgTc)iOhO3r=l?imNUR-pWl!ktO0r_a0Nqo@bu8MzyjSq9zkqPe*`Sxz75rZ zr9X%(=PVqCRB=zfX+_u&*k4#s1k4OV11YgkCrlr6V;vz<{99HKC@qQ+H8xv5)sc63 z69;U4O&{fb5(fN``jJH#3=GHsV56@{d@7`VhA$K^;GU+R-V%%cnmjYs?>c5^6Ugv} zn<}L&i;2`zzW@(kxf$$gVH@7nh}2%G%ciQ_B?r{13?Q@=Q+6msQGtnyY%Gkjeor?g z7F*tMqLdhcq+LCCo^D;CtOACCBhXgK-M&w{*dcUdmtv@XFTofmmpcWKtCn^`#?oZC zUOm<QC1a)+;H2Zve14RDpR!I0lk^dqc$N^fU^W~mk(jvhB`mqitWKRippxFqPzrU{ zcPfM6W;1_A@B+1@Q@wCoST-~IPavhxX0v(*iG^+o6rBoLe`MUfYuTRB;Z%+q%_7W9 zDL&?t%6o=@-GUYv&qOcCS7Jq%$^0c4k8~_XQ!KC59PkrIAYM@@%s1+f=IQR(V=LHC z%wM}Z{MQ%qgczfQV8NSMu%GZB7+oe2hF7{zwV*g7I@VXaE2gtl5Lew`?N7JwN`c#j zGJ#z(oQM*<PFAKf5l;#Zq5V=H`YZ^zv~o=QTq9#9<5}YZdauuPj}bbDb-O#h*W86q z{H+cAsE<L!pBR4fwL@@pOUY)4uiBz6R{Op7WryS&*zeY}8`$_01z%)k$5aDy6h>52 z7sK$hR|Vh6y&pfIUK&!`8HH*>12$nWA)Y<DeYN6}UOt4|m%_aJ%g>np+XwOj=jNLD z{QA4gezbe>wiP?`jJO;c&EId;=2u80s_r97;TX!6@*(<%WL+^bmxheMB3pKx0OpH^ zPs}knV+jpJ4TaD<VabV^SI2-ELJCb9;Wwo$^++$X&>@r^V`mTsjf`7!z^H}eHQ#Rp z72(>Dm<W>#QO!ZYR*O@yHic`3*T^t7jc=d`Jz6Lk@Y-bL%cOp_<QC7R+MIh7-+O%L zgkh=?9YCZ&fDC@~yOR%d8@e|4j>~=#xzIJl?`{Qu;$uC~NkePE+7wSW_FM`&V{gFN zl;lq@<h8DED3`q8CPI4MvbTi2f`4<t!PvyOM$}BRG$~#ym$=;0)Uz8BkP0g`d^lAB z9eZe|3-spiVr_U=XSM%rOw#PPMg8{~zoT9GxpHsrYSG5L6|SD*G{dhC;l6F~-YLy= zB?kglaDe&CNDBXTu}}wHUGw9c#~06I_<D528$Nj}tcO4&4f#Yc5Pxnklu5?5s<?JI zTX?X2b#fynjR<V^G7jfM0Jg$ROS--~{@zhH2B?r20y{JWsidw#>;FtAsl!h;tnOvj z#gYx!q$5MdZ0Jxjy=t*q)HFeeyI-vgaGdh1QNhqGRy8qS)|6S0QK7Gj9R?Co{Knh> za>xkQZ0}bBx!9@EUxRBYGm25^G}&j-`0VWX04E|J!kJ8^WoZ(jbhU_twFwWIH32fv zi=pg~(b#ajW=`)Vikwwe39lpML?|sY$?*6*kYBxku_<=#$gfTqQ_F!9F0=OkHnzBo zEwR!H_h|MNjuG$Tj6zaaouO}HYWCF8vN4C%EX-%Iu%ho;q$G#ErnafhXR*4J2Rp5* zhsi0;wlSwE*inVFO>{(8?N~82zijpt+9Y_-^>xnE%T*zk9gi|j7b@s<5{|qEquUD( zS;<Fbn&#?PgjjZVRL=q_J}F4-9UJe~sZk`O!nV1J6>-%RySZOCOEh*>!kvbsQ265* z>X8*_Wy&~FB@aDHz%glyiAujXq-|2kDUjFTn9Rafsl+XNyFP%PG|l&ZGWBcEXxy=9 zeDn2PIoVuL$gX0RgVK1O$x3%pOzS7x^U5Pi;mtT)%cY;&e&M7GLM}zP+IPbqLt=^5 z7qLfri8myf;~2psc@^cA6mG&{C%e_(M$$!wC^5p^T1QzrS%I?(U{qcd+oJJkQxe10 zON{Q*?iz%F4MbEsoEc+x3E?&2wVR^v<KUU%<3!et*S>3|Q0lDaMvgS<qzNZgY{&J_ zJ#Tdj1)AtN1=pq6h55{9v@1MyP`7ASP}AyRM+m39hYAl8mQ)&$DGj<r+ecC3#7Be? zWGo%S#WJ%U`uhf^QmjQriQHc6^wTJdf8k-8l4}Q1)_-x!L`3vV7HMb%LW$R1jTiA| z1PwYCHr{Bbfnyi}Nu{MaC-!}p2jdzNqLY)eivRGY9yqhnx@YUeM3`~hN3!}Yd~D;1 zL|a0`$=3U@Xqya5lz32gaS|&AT$~5P4l9f_<fuZ^#NZ$HFh;|sEXaw=`Qa5K$4pL+ zk`kG(wcD?O7{3Hu+25!(ip5h&(aJyZAcBGf8xfw(fBcby%j^P_hiUx#>7mNjI{2w! z9|~=!83T%GW*iaChSS!`Xd^beFp9N4%K+k*j#jFumk}U?=WKL_kJAltxnxp~+lZzT zp@&&kSPTg3oSGos`rVBhK0|4NdHM_hnKuw1#0JV{gi_dKDJLB+ix~~HpU9%jD)@YY zOK)L7kgbLyN2%Dx#fuY}8swh4ACk7%BpP-n5(RhDq{gEHP*Fo4IviX{C49|B5h~SC zFr`=0)=h2^F5UpCAgt?R5u{6V<a5ODjWDGfTC~$_FT}rgG8yDcak@wvkU5wL@;TeZ zPO`GR+!M%zf?lM1u-<{|;Q(fZw-gDSLQrBP73s%I4kriHo~I8%gb!B4r>vpUf#*nC zCQ`$!|C;L2lpjlG?(>T$(_$O3_YNNbPT~(?!j3aD8k=yu^ogw4bkjvgF|3BOq(hB& zG;^cPXmcUP$ox8zElCJ-zMbK9q^8{rri#8Cek5Y<n!J9a_;CLF!lX>dr0YT-KTh@J z6^AcB9ejew8BY5kzZUZX(7Po==eW<(;uV~E7(BY5c0^xr`cuRwn)47bN?zOb!0?cw z#v}R$z66&m#+AHfo@(^V2#S~bhoUkkTArg+6w>JzZ52r96^({1W!?>4$h0l|-jDfj z>7(<+%67#(A|4hZ3>Y;hd&S?}F;`Vtqz|pK&B>NJ=Faci;gkf-+GmfQR8^zo_vul2 zB!)kfu4Dq_g)8TBBo52*sB6F`qa&JCR=_A$QWgX_K}fZm{Cb2#1q`^S3+WaS>sS#@ z-4k*G=#?z6d_e7JJ+Z8^(t0tNdL{K5F;2nfQbXgld}a(X)Gr;WojOy`^?es~AClT$ z5^lD{WJek0!p-QEH5E7n6DKQ0%_ZBZ=|jfV_MM{VmL8y-Wd|>OmeemP=C@xI@@M~1 zW2S*im@Rc=O>V886_UJ@oh1!2H$Ku&U*Hh_oxd{32)vf1$cRiepv28ricM;}#p!+k zaK{z1I=9Y%3m4|Pj*BD*Fn5Vh?O@oD^1UcjyeNh0fbhh~V<H!nK^g9ls(UcBEXK%| za;U;8!rSm)=b{kqG>6xb#4njlGW8OehUe!MnoR(wn#nsoyL1m!Rov)Nv4~&JEVl7L z#^qYdTpNI#u`N0UbVMiDmD>g2VQcG3>4D6<e4?4s7RYh4$dWZU@g7b8WX0r`Y#b|8 z3YQ)JCB?6yErIG~7k5+q&+P!y)4{ysbsIkYV)dCA_K*X*S_YZv$~E$4z?0FEN&a#6 zu6U$Ha8ZSpZ{-B6MpRKG`<444i}FgV<SB1ctW;y>gErgddZnSQTs){BExxRJR<X^- zYm(Jvr!t=*AyjgTOAVJyQV$F^aXXDzoS{BdiAO*9ilg~q7RC`nC5|tGI_Uyg6q+Af z_~)U~w|4zdx*se%qb+sj)C^v1tN;D8ay1fxZE(V)?t(1s&9p6pA7Hdq5VZ|AI8!`5 z5hh!uE4{0FgUC<qp56l-r~_8&6{D*VzZZ@IkW;rUvjYN!wSrS{8xSFc>B?bIxTdZa z;!S8FHJPPiIDQ*FAUiW<aE@x^o9n9|8jmg@-NK{Bp?S^ASxTeiKt-d+p<~?wB~$$6 zYs~@-VparJ8G|Da)YdPaT|JZDM=~!q?}qMq3t-C^QrDKsI-lJX%$oxhq5C@Q^duDg z?4%^g!FG&#N~t%OMEM|YwNie=r=BomjT@p{jK5z0kxB5!-&Ti1a4@|(IkYUNy!rwm zA7fW)@@}CoPb~|!N)(&5w6qwth}CAD?fnX{S&nmHH}F{(r2k`Y>SYnjILFjDvxvSC zk<qtm;E%gFWTR}j-)ETL$1j7){*CDwtvowxb3c;!9Mg7Z#rbtWL$XeH?y~7uyQWbt z#a&HwZGqZSS}oy`aTL<nVm#5RN^Qv@JMl}plNYWNMy?VPsEuV%HksMQZ&M@BDCAq> z=j4Kx@Pg~&2Z?cmMDa;)#xVeorJrxDBqy{+`kG+ZPQqC@#ku-c3ucU+69$#q_*se` z-H#PFW^>-C0>++|6r=<$Z8)ZFaK=ZjwsNYXqRpl9G|yme@Eld5B-*I69Nx_TResHi z!5nm+>6zaJYQO#%D{~o-oOJ;q`fa5}l!<gWB)3)MwB=etSu|A)HNQp#HqArvXJ)-9 z_RMP3>8G*U-E$OM&7@dqciBCWtd}|SrDXz$TB($&m*=Epuolu2k`KUwO7maP3P0ok zmF57l<v@cb34lh%^P~cUHM{48n*rZ-qaEZ1MzzCoG~#m{7z+O*JPL)+yXEB9Q1-&3 z*Ms=?1?R8>Sh0Ba@&sO1iZ5^+3s8{B8t|M;Pg&O+{tZJCiLWd6H@{b~9{CLF9s3Kn zt5)Rs9ejne?o{%f><hmvi~%iy7ixeOmE*g3u@{kRhrlzjq(;E}*Ab<!Rkl&Tp<Nu$ zj_BI>B$Dl%X7fd~KY)I|(pxUeHj;gNsK6;ZR>`ciu;GxvhDUt!+31Knss2U(%ts8K z18)8;<2ax9RG?!|Lwdt^i5L^&O788roKmVAB)=EdK~HqR2Q=)H_VW}xY=95MP_Ov< zPEz3%DRK}+(aUBwsr83H8>`H^v~|A_t}0vPmRwKPt1{|qOY|PZu}j9+{ZhF&-H_TB zU9xWLpNTc`enI|)h9jQeqf5RfGLFk_vfX`40iMpd%KZF!lKbZTdBw$<^G6nuS+$fT zrbK)xo&;buPJcpOZ=x>n+bRXVFDs(23Xr=rDE&!)pVXZ;;A07NXGl_0m`{Z)DQIu$ zFDvY4xu-ifTe_$|n2B83eI;KUg6pVbw+N!nyLj~wnRi{4mNy{WDV)G1!6$y=+x6U{ z%4_9=Q^L!x_gAYp?J3+u5hA5cO8aHeI=6AC8^S{mzhqCBvBLYEutUC(X0>hKg|AvN zvkmJCQNA45_KjW{aEcyrBppcO6G0zTy%v1&@~+2!n?kA9?>0>AjFN|JdCnHQ8$hEU zw#mwGifHppLP?89LMb(Y3Li9iCPx7W%ek}2FgD2YSzjsR4Xj<=zN{Yo@7s7(k%mP4 znT2p&<j^yvFM2RSnHHwMMc(2UdoUNS2x4CzITQi_G`d@qyz~-_^u1>4EQ@q_chd-E z78uvD*C@oba`U3W2Iw`M#`5C8jOHv8^Li<|j^SI>>>`77Dp71Vtz=J?4Zck4SdRbd zfF}C_>Y(#)r@y!Q0`tMlG#b9>5`fAI$B&tWJfbGlYW$J4V+-s=HH!`+;1XeL@USdx zR0$G&&XBf9lQtkH5)p=U!8J!1{oc4E!N-~A<J>bxl<m&B1N64_9;PGPY(a-R^5$^; z$s$KcZ@+yaMM3@7vA!{XqU>6E;;=3-hMYZ+44?u}zabmCE)yB?*_w91m$n1Yskp&@ z;kxeJX-#ioX^{elyLu~gzx|_KxLpX62MF%Axq3$!Z_P`pBWR?zP8OI`PV~6Aa0Oi0 zv_Ot1m&plf-ZF{e(z(Ms3*S5q$e|j;gOwGrmWsCHf<WiXqr)_<#-^P7eUDy;3|#TD z>Li(h8y?g<J;67jdFW)*FQt@{ZRKdyHS;bpPDM~lC-|XQ#9ez=^9^R&ttvwy+?%aa zd%wnUga`n>c$(2H{884C1FvHQQ12tX=qFUsK~zM!W=K>;zaRsu4Xmcc@8nSs!vK+{ z?}bq}-m&p5jRSam67n>yG9ez=I^|J1O;Np8s=P~9MXYLxD+cFQK7PhG=bkjo{Naae zjp3NWWrlFWDb3Z5D07Q|WjZ=wOQ=aKA%en=O@hL$QCKpIXNZE=InFk|Fhq-&H!6&X z*MVy8=hL7Aw&pQjHrFf27C%3B<>FX{@f<FfR}de0cdavaWPgv)j@|tVyBnBmhay-w zr|b1WexK9-QI~=CyWk={v~fqpT~}natdz+o<7km0b~X=ETaH&3c8K+WenHsm4$JbO z(VV8XuzE|ddkZX9Jyu8q8}^_*l5MVd3l9D~ukx-7Zx-9b=)zAy5|=wv&fhoX&%tys z<My5<Y3f7yT__~Vfd_x|p0}LjxtDuS_R+I_`+x_Y&NM2$J?D-FRpnJiUe1#n@yYE< z`#UbDOlhY7rGj<NITWLL^jTkEme5XKSF5;^iIAxeZLh<I#Xa&Fa#{)+r@~mX3V$m$ zXDY{S!F{qy3{p^j=X3Noq`tM--g+jju*&(g*4VUGd0gwfGcUfw4^YPBCewnah2(*v z-_z~yyDrSMxMprKB^h|c)p!>OLNhUoxL4*@nY}&M3G*T-p6<k?^{(XrB}ewz#nq9x zUPaq7+HwSFFH3OhCiR(jMzu3;PQU~Zu~qxb%Akj9^%3YeC5M$cxT9h-$YV*Fr;>7a zo}~_&yGOB)#vbU|Q3FA8S^X)c-yBlmN(_%}`7Ha3uWFe?>9f=3hlO{^gv~$p`v?vk z_P*r43|(S{%ihs;)YH|jAMpP=-Ms7Ne75_YZZiL3CHVjSU`X1|?Ehh&gA=Xn7W7d@ zf8bM9Y>lG!`PWFDDA9G;x*{1Eh^55u66*9D+-4^dYZ{xXP@?sQ<?=<%4xst`@F(1J z6ft91q!t%X9cO;rXn#Eq`2GT#=V6M$v>LVrY%(azM;C^4FuN7CQ%$!3sr1JL=!Be& zuOZL^bLp$Qo2rL=WDzQIls%s<HhcsSZZlBdTXM6b%<%FtpBuLuS#4c8jK+EW&>!Go z{s}Q0b#+#8bKga|01t%^9Z=wEsevvXM_{$dCR97ed3@1kX)mtSS!JN^rtqKOj}p~> zfpCI@DX*DqcB6ZnBcl~}sGO~1s$AtfkX6fy3N8*ebvZc*KBW;dA=)?#BE&}-or74i zZUt5;{FBPnkZD8YUXDsx&2LvSziAlec3oc>&Lf1Doc3g?H9{OO_$M4B0qTat0UsWP zTlxUeQ3B;oJ%en4n?zQB6*Fb#wH7`$SQN5GI|=DnJKiYm{?-?#-H;#sIjz7kQ4&VW zN9d1(1$_W~S=<%qDD!mwRytas=eqX^iW}YSx3;wJ#)Xp_`Qk1DFiXac$-3;jQbCif zLA-T_s~5yP@Q@W>pXKl^gipQ>gp@HlBB>WDVpW199;V%?N1`U$ovLE;NI2?|_q2~5 zlg>xT9NADWkv5-*FjS~nP^7$k!N2z?dr!)&l0+4xDK7=-6Rkd$+_^`{bVx!5LgC#N z-dv-k@OlYCEvBfcr1*RsNwcV?QT0bm(q-IyJJ$hm2~mq{6zIn!D20k5)fe(+iM6DJ ze-w_*F|c%@)HREgpRrl@W5;_J5vB4c?UW8~<VA?`+oZOidfO>%o0)(A4`%-yNk1(H z5CGuzH(uHQ`&j+IRmTOKoJ?#Ct$+1grR|IitpDGt!~ZdqSJ?cOtw-R=EQ+q4UvclH zdX=xlK-fhQKoKCPBoFAZ*(~11O6-tXo>i0w!T$u{lg!#itEUX3V{$S*naW!C@%rll zS{L(1t%xz(*B`{1NL!*aMc<~fE=g;gXi&Gb$HpD!P)8?JzfN;4F&wv(5HH<=c>>)n z({271)xREH89=C(5YKL{mmJJ_d>qH<OHp%o7e!U>z;;gTvTlgM*vz9@YTTYZ#%_2A zS0G-t9oMQEpvfv(UjfQ8T$vAHi)zOj3>D*{xSRiu3acc=7cvLyD?_ZObdu$5@b*!y zaZ#u?7uF}SrHVQa=sTOhGW{6WUlq#RhPPm^GsRH#qlX8{Kq-i~98l;eq>KdCnWyKl zUu&UWBqu#Tt9jQ97U4}3)&(p2-eCLznXMEm!>i^EMpeVzPg%p;?@O;dJBQQY(vV;d z3v+-3oTPC!2LTUAx^S2t{v;S_h(EZ^0_dS5g^F*m{TEIy^Qal~%mu3h7*o`jWOH}i ztv8M)3X3a*+ry_KkYXYE4dB0?M|t}#Tp+(<S5$ESAA`34+{^ec&-g!{sOtG&>}6CQ zBbq;xhoHj}b@j-@koDB#XcCY~>_x&Y;i%MH|3tF^X2h{36UCVfQ-;oEA+4ZkJ`^Qi zQf^8}6eFO$Z+Dj-F1wkG##tTx>FjR2oOXFmbKFj6K3+=kePQ<4d7%z5R5cOB;zO6| zm9^m#U4lcA;7t&*=q|a-!`!)}SgY<L`cp6ihUK`T5NaMCSnyVawc!h~cVP~-UR^PE z4MN#_um@fSUU_pM4v~EORuYM9?;gwP-|v~>XT#i8hnxtx@kaoBF$QAS-hT7N5kH^l zB^i+})V>L;9_0Qqf-dyF%ky8Mp-dp#%!Nls3vCt}q3QLM3M-(Zs1k}1bqQ9PVU)U` ztE=?;^6=x}_VD%N@${>qhpkU*)AuUBu_cqYiY&@;O$HV*z@~#Tzh?#=CK`=KwBv+o zh%<IRE+<<<>z<y<Li4fUga&=eks@7Fc($mDQaoiTsNk~-jCT_fyXZ===ne-R{=1}# z@)Zj}aHGxc*4Yp=(AUu?Ad%}VMHZ6{+EWxG-I-*RlF4@3iI52=yLr3niln2yBwG|E z+Quaop&DhBKQ6j0s<UwrCJ)SEYGw-cEmF-mRxP&%FA{=PWg?q#>u%0xPKYtyC)DaQ zpDW}*86g%><OE5HGA5d)(L$h5ml-x8zbWQM`Usu*u?pH!q)+;)5&VPX!CDcez$S^* z#3`A2VXirbRluU7y}K%{L|b`exxi2p=v{|QX?!!pQb*3DwTJYF|E6O&c+-)AhCdJI z#WtL?K1Gc(hgV?HpCE`sYDRB-0=1T$6SlZYPla@aT7(IA{VSs|h5rHqb78I$L~Rg| z4q2vN5xOy5hgjbOJxZ~Ahpn5!J$QnDNDF8Hg-s^(<p1jII^e1P-v33)%-%Dy;*!00 z_R5xwgzRfwdq+aZ9)*k>BH3IcWMq`g$j()0kWE(qkIL8A&A0mf&+BzxpKF}=`#jG% z&*wa!&pGFLs5_b#QTZE4Bp+})qzyPQ7B4Z7Y*&?0PSX&|FIR;WBP1|coF9ZeP*$9w z!6aJ_3%Sh=HY3FAt8V144|y<cjLG9Ni0-bXG-mrKlbq21l|*9`mr`m%i0QIDabwaF zRh9o84|M8pD~Uba>fu}IAyYHr1OYKIZ51F>_uY^%N#!k~eU53at-_E-Gh?ahmM5y* z+BTIbeH;%v1}Cj<Ywo7o?8!D|Fk8}RR+oy{*(Dk3Rn>o{8d%UeSMWg(nphxEU`sL< zQR~LrTq>Da(FqSP2%&^1ZL#DTo5Sbl9;&57tQ-@U&I#lj)aNSkcfEJwQD!33?anVU z?pw2q7WtMvfji493`rSFnyp7{w87cW`ak=UEYlk5PCB1K6UDVKXyozOChH4yHh~Q< zv>yvKw6WLfi!PZUx60JZcTNM7jo{ww9b8Q+S7C3W<Q5t=K5`aem0H!-OWG!yq&T`w zL9<h?vUoP1(h&O({NHUvM6Rm5B+4?c%WJfg#dg+r^0_A|&}s~}*2gN7n?^0YW1}u& zu+)3AG_tNtFv-SSZ23m_(^8&B+xcNQwuoU>A5&llSwdwh$=Q(*(f3ofqcz=nwOmOy z(J!K=*wNoRU*${{Mbwapi9pTB(&VVKefqd-qrUb9*Eyr2E@oZ9Cgf}Mc;QP<0D)R4 zz=!*^VIG4T*7Xl=sJxrWv9hW^eJ%qYp5(d0?E6LZzJ}=7E+1{?GQA;z+!^VBD81}O z0kJ^dKy&WMw+1+aGVYY-v@i28@Gm+sX5=@U%F<J54B@9m<FVM{YitYR8zS_J_(KGH zt8{`dm2X@SVMym&+p@{eE({%0KP}+LIOe-)zv}kb!d%-4Z9+vnDB~Kg&+w<3bq2*5 z`u8M^L$Yr)vZG@|>=Z?W)oar}2~Rc&F|+3A)n-U2GF10+QdxDb^iA@7eL$c7yhBtL z>lABrh^qy9XZ${E1}Ss5!N4;ig0-pUh6@|RPCHOWvgG{|l}2enRgJftsN%D|ck0YO zuAQd2aMPSyGuJ~jm)aY=+p~mGudw4erwE%P^)5f<*$$2C-4^I=e8-}7##ZQ!8!Tep z+Z_!}CAI~sry$|XK$ktXaxP*x<_ijCPp`2=6sNLZU<@9Sz-rz7^BCE9yh0jV4(I!Z zxmA4d;>B-!vD}Xp*&*N%`b^e&R;D97WS}{~{O-EtXeZNfdf51tw!WR6Noo4hjHPv5 z?heYYRSBPjMc}tFEU^|U8a1CxxK%)WTcn9P%`wR^I$QSeMn6=w>Z9OoVvcrl`zYlZ z2y`mAu0bV(Scc>G_EmIo_<J`spJ!5|B|Nx9;jXDp(3RzE_|)z6Q%~Z%1o9xC($B>4 zm*~h`mxYZC&+U>C5G1FZH5L^U>Cq-9UDRQa35jz&NBj*0{uJKf<TrbDPJ6YBjYr1v z-Jp)`sw@0cJWU7};Ty(N`>Zs5=Fn@&)Xh6aX(H3w9m9BGLePqVotxTeSPh5-mc7$# z-80t6yB0$Nx<54ohdO*QL7<B#`%1`peiY3hz(Eg}A2Vu{-o!!7+HXL(jB^~|UR2zE z(mUX3-l7N{t&*hE;VVqitm`?PX7@QlCg39p2>m_(&+#*=eoNiYDB4rE<IeJ!x9fj{ zjh5~&GUJ|yRpJS6j=TELjk^ZSP2S(znUdT;wZzbXok^sLPJ}W@PuWC1dHEtmpa!Km z3ah8K`efW_!c7}=UaT8v)>4Cag@qfyZS};<ARP|HEzxy@RxNQ(L<I2*mst4CLjQWI zCLd4J2s{{^xsPthocP{NlAzfw7vFOtehv_S_h<$Yf;yR*!F%qq*m?ZC6w#tpX3UJJ zxHCzqZhQk*2K$ALGdFIUQNBtEWEm`HeM?iVXCp3VnX;`4F_)_*t4OTijK6{jewsfL znno67!eVKGzMaP*N})bFYHNt+IBLk8Gd8`YH`FIMYk!BRy|+C6o>Fx;Vf1;oync2k z9v#-<l4c@#!@Fz5xx(#=xAQ7-W_Ck69p*<vrAlz9czK2M-ZH3`lqAJT3Q#>w?d6R& zOI`CCS_d=tf3|?g3Z}b6-_Rdg3y~enQhmgkni0Cvf9m6%Ft8r;NC5|b%t&?lkl*4{ z8U<KR<Ur9&bCcU$L?%LSI)an9N5<hfOhXjYvzjrNO9}$J+=6Q1v3&e2R=fdgAB-ed zy@TM1<wV{=uxJ*j@8!?}Pn10LdmBTkgJo<_9x{X{H1*jMV^)Y~b@QZWUB~@&p`T|t z_QD>i^;Ds^gq6ti(1xB7y_$zA!i-M~#!!tl$ErTR>P~>T=Yky)8(uvPbvLmB=UfoD zrfl}8<1OQrm?8#j1!?s*T>AoectQl&m!o&*^JcIW`_&bk3tN}k^0rjl=HL$z*uIYt z?7l?^Dqr?q121<k)GkW4%te+ZZZ$}&Ojnh_9S<Ka*4g>0Sp$xoAy!&{2^{^Anl460 zI&7urrc&|Y{rjv04VOl{y7c82N6xzg5ueYmQ(q(zC3w_C#x*~%<llZF#S<oTCg{?d z-lJ;;SYXIrr7stvma)3=TXZim+stU&RurLEk>yf5j7MI{W`tsoxzA*PrmK)cTskU| zf2C}Bq$>S$-1JgIh0aW@LxI|-8(OGuD#^M01ghh}&#ObO>tZgSw_LW`zdf&IN$YO# z)|X_9m#JwLW5pErZB3ScggKcNzxA9(hyKkK9I#pR&79&*+SV_eu={00{HF=Bb+AEe znaSof+r1jZ!EL5XgqXWkckaFSSyEk}o!%p8XsD}O>borZ6x%X2b&q!s&1-O(>`kZ$ zB2l^5Cx9xQx9)PXN1xPM)@+LxACH_iZ8zGc(>wnFS_O|@hKsx<!FoZWaMg!u*IKF8 zW}P3`h~J%C%xvWQ&@r<W#x<X_L1egnQ)1Zd<|Iwp+BKV<KJ_VM&khB_(^t0WU)7r9 zw~$MVS2GGq-pxs9pKiybey+q<WAD!Wk#BF}Jbi0Er2eIIN;!cR(K%ri@<6p7aGCf0 z)PN@8U75jRa+mP5clupy75MxelnnFqiyW0>pMjXOzLEa7OvSlM&&G9ioQw9~RsD4F zK7Q+_&|Q6{eZ^8Rx@pKL`le6kH+(fLc{=V&{b%I5=n}VHV4)X_2Y!pYxgC8wU)yP! zPF3t$?(jsC>Ge=&{kmPGUEETpaw(QTAl)m#{qR3_aq9!wK%6XHfV4C>Y^>Z|%ns7j z{Ja?^IA{+@;kR#IjHx<p9h8LC6`To156^y!hJpG%ORFg>kar%3$eJT4?xNBKUVmoO z`A8Zo-{~_;vcikZ(p}EZzU4kO6W<oOs*`uO_hwi?s!j4Zh>PqkMyE{VvS?;44Z@lj zz^fKX9UL!8Wc(9VgI?P4*zpis8dzl};I>yr1>dtXU=FTAlx}Eht4-*7RACL^AflGh zyZb1hTf(~CkMo%#Q%NMgM9tE2D+)joqbtHYA89Ql1nqVTt+MxZ^*FRd&n5YlIi!8m z>$Ysd!l{+C)y;Wa<K2+e8*SV+PaB*>(ZV-=<+NZKV;v4mt}v2m>`v$-$3b;GsLxf= zd~f(rmfpl``{0aVwN7y!>eGyJFP`L+TxHjHTOS{K^$L2`@6(Rli`{EFwpH@R%eZ6g zwf7rc43<A*1Q;!xeUQ*$(tU17{YgRqr7_w2CmHs6jLPaaisvGfciLYJFL?|YL0TgF z)vZ}W3!dJ=e4h6Fj3j~#k6~XHm62*Z#MxeGCd5^o!4iAzf;j6aZXHVgbJ5<JT}HXC zMa@)$&VrHK+hx+OjZBn_Lg_G6kIcKz0^iE?ioO($_K(nSe_mQ_-#vFnWk>Yk!=k;{ z-Rn%~B3amGr}}SxfE$vS8FIPL=Qt57$|R#sSoFgdNUT?fYOYjPl%ZB<Dg>Fpi=<FR zh!tZQRv!qGd2w-d%|0vjpKqq$M?q}ig-a3Xw(1f+y*U>jq=DWby7Zxm@y;B<89!9= zbgEH*Uy)~iq5kJLX$+ps$kV`#6jW#|9BGz^`ivNeid(wVbk4jl)VBpW&~;eXNi{#` zwx?{DXR~*sqQcFhY0XCfQ4-*2aN1BGX>$_swtKEqnd>j6vcZ!#0)pXRi?<{!P?tGw z2x_`RD$W)qD{?z}VDPt?+)8*rqLWFIPQ(9-VbBdf{7ff?w9CZ{sIi_gnuC$I0(+P8 zms9XB%}VQ>>p<fUdl@Vy-yM%1V*%pfJY_Q@oq;8-!>ve##}jog6+cD?v~n4Pa9Vmc zg#K<TJpru+0smM0m_?9<3<lwQX+7Y#ZS<P77P$Ov_%Tq>$|+`adO=B7`uj35Y}6EZ z{dY`x@w8;R-7zrsr1O_~Jvl*|o-x%jF=Rr1C}GXP^|IYN`1sqmG-oI@R#%X66c#5W z$$tQB)sqwiVm;Y^`Dw3mo|firP{*HsOQJre5%Dm^H@we0FN88VWJ0dja?_U38z73f zrCV!b3qNP0kM#%9T!W5`ynGcg%BL28FW1J-J1_S`BJGCaReQ!am(2%qZ3lLgzq|ns z!!fF@`0=*z)J2BwZ*hO|Yu^cI_nF$9l-Pb3jE7=P8gZ#!xiuZ7-cSa`gb`6mxGTgg z-DLdID?M!Z%+hHB#{?&0$GFRpf+_}q<_wbzX6K?w;%6szz1RbySDSr2r^h_qi$khs zXdZ9A0!_Bf)TR2-^-K~q`FQ!#1x(U4VbV%AA@Ei{%cA(EwC{XfjRi?`&9rav5;Q5% zO1`Rn@OA_ZB@N*mC#)?d3P!}Eh;=NgpIKsy{(yr`hv=aouwt@r&P&}Z3DNWo9ro30 zX52~(aTV$*HHlgB66-4GQru!_AZ|)V*I5X=WG)`N@U&D>e@@C#V@JwEL*L`7#$yes z62C^5%Qniaow2$3HrAc7U{qzpb&FA*xLI1JSWR@`RF=JCcvTI)%dH7;sWInt9JLu# z|Ao|Q?K)cD<XIH`HHF$U*`>g_JKsym=joo5gR80wtv01N`um1nQ@Ms0Y*bVzxL34} zo?gizp?`=Y{*W>^Hy2%Jl)y?A+&7s1UVHFixuIy~sawXjcDCL`129cK7|ZQS0u;A} zTJC<n>#WNmqkIrnHpAhHVcM(U^vJA~dl@jf_bs*3?i+=&vuC?Aiy_pcB~=1syDni4 zw+FLuz>F773u#$;NUQ9WDtUPY@+rA3WBhQdKFKOyzkA(URa7;4tW>3jQIfi8v0h3g zJC_HVDXS#>DWb|&se7FHnr=q&<fFndMyX6ok|*VZ?$(NG!W2uXIh0KPUw36VxOJEs zWL55mPTHM6#qp$QRV3#jrg6AO-3EUqlT!W#^D7D+pA>l#xg9o02}}u=b-R>@sw={Z zHF*?t2FmhqZ=|qa>x=A!*$S+0T<RES(CQkwg0f!ut%n<5m;I9RK*Ok?E82=ogcAWX zVMf_PEhO%Ra)InLoTNnu*N)LQf?H;Ub+bfT-C6(^c<%)T42I|Z))X=BQ!8Ur_1gV| zIq@p0@`Lg#&@KI;S3rcoc+0%=cpeub%lgbGd}9$GOX8GXLMxQ<V2Z{eubf-2zA+uv zklCK%<D%OZPsbqt7)9|B#TjKk_;XlT@qi8gU;-qC#!y7fw){$5w)b;#tp!5kG=0`6 z9Ik64yvf9Ei%-l@D!sM^YDUjdS=D7mk|C%pMhoY!Y^d$mD?YDYA~!}WU*52Y%N5AI z@j_K9ct+crRE$scRft}ZVlh^m8$*08g%+MBg@9IR_jNa17qs|g2jAO8e#zebVs`5C z#M~6d^GVBMYaN$IhQCbj@Py)%Eu&FLw$AWyA`~pR7i~dfi4_-S+QVK5Mc%jA4e6e> zhO*D*M?NTf-eX`eO)9TIQu{7Dm77Acnj4b1jI9@c*ZL8wL%8kLEhd$KM8=Y!fbN@9 zC7B5#y>JM1n5M)!&im==EgHs2j+xCZG~+~QWCi?s!QyFo2kqx{%jE2n3^N*Ayz6Lp zhg5g^3#<s8**C}4WoKx|EauIJ1o&O{zsW4{WH^4j7~KJ<QRtxARB~N6G1=Cq2xytI z+zswgLp5jEXPYtIst)_svBi}Uvn(mbhG0wms7f!xihoPy$`YnO3OL=n<3dU={6=)> z+5FoJ@$u@9WJgPKpUWEd4}4AK9TJKU8W%ms!d0p%OIOX+bY+55zl!vIaz$XFI9Ep+ z<dS$zNm8TS5RixZJbxTR?cH|bfw~-cU9~alq(f12VSHQ>;bL_}7PDI2Y`Ng*XY(65 zh0%`@Lve%fc;)N4_g12bNrt6gH=N#OHtxO`$lpWlw=Z6MF+E@;>GkZ#lAZTn`aHwf z&I1|aV#b_VHMIgBN*RzU9i@Z@m}0i>o?({&%fpEfaOpFeaJ7V37;m0?kzd}}Lk@9$ zL}8TEo7WZAcRi%zFZxkr6<0k#X-;lTD`Oc~cDb@olwgWCewvk{GJ}hCXbF!AdiLpd z|Cck$ZTKI?Ack{34Lva7+k=H8K2HTZiurox6F+>dy+@R9T^awxj590D$|kXUg+Ygc z(f)jlRwN(4z$#%PnOVc;#Fv{nAi{#UcXPNcmP#5O{zh_*`=q^JCeia{sN4zHjk2*y zqUVh{Ya{j<IKA2W1mW}eeRalbF4<$oYZtObji4#>>SPmP^i#Qfcq_MTqo8g52Fi^F zKBc$$HVI!xFx*4Y9l+nt)$AoZORD}%5I10oI3kx`-N30QueiwIw#0VV2E*Fb-nKW% z=+r^hos`Y-7~{cA1FVbK$_=~*z53+Q8KGjg;>ztg((H12%QTf4OYU8y)C}h5yo#$% z&Q$`vMM*g?ZcatAn2j!hFv8KuN(dw)T*}sF#THDHxo8xC^?v<bx3iehloREh7QD>J zc`U6bVo~hOr6I!8*GTZ<^D~;unKjK<lSb>=!IR|<CLOcJa^Z#o;e`&fF86DiwTx_5 z^+xIq@90~tHVYK{W8uadIIL1Sm<$jPsUn0~E>GB4E>Mcvt*2GK);93jIDd<(nNjHO z4Hi@2^%Uyx<t6q~e7n*&BG#Xj>=^Z~5eZ!5rO5%4H|eFoNj<JnEw;I(G_8jWC@X^D zfeW5#XW8dOR29iCD{XUCxg!{eaZraMSGf#$B@EDq)OE7ovZ1oU#K|=2n|sW8oxhIE zriGbgdm8i0QQ$ne-@3gT)BMa$`%TF(rNHc$Z=9p67+syKBYVZ}V$K_l)P#)$nD^Ai z)i@@<Jsfy5s4!Mrlao<acWb{oBXF>D#+Kcu%_57zZb4Z@Ak#X6txD^{U3wBl^r+W- zLorkK;uc;NgTj7dGxHQS+@T*T>Q*j4^Ll$ejQqWrwcHyG9y%Mk%m8nBVG5hvSaYm5 zJN^#-Q46kZG)@T8n2^QCjxIwxUVi%s>EY`E?#@_(A~njFrTiDq;8v|W-1jT|ROlNI zU$h|YoD4PVTE^&NC6_m{EAFBVqsM`P*`-AcDGWQygURzM32Xeq2xng~XQsYeTZ5v$ zQLaa2M_Iplw}4eL6fLPu`6`PYcVMysO>`{8CB~glD=TX7?JZcHfHNmykBM?QD)#D) zGp>R*<^D?WhFQKRc^}22l6F=D2RPrxaX2ZF!b1X0XF*d4%=!sbNcS1q2WOUE(7e4$ z^L8f;F)__d3>&KQFE8%$I4h^y5FYBfB&f<E9*wxTo`y@*Y+nk_nU{tWTDqRgI^8*~ z?Bb3&J@i%}j?QgicjYnHi}D5zkFxgiu@3ghueSBgqa>Wzn71_OSrPe-DHV{O#Q;GP z+Tw!J?eVjX19RKH?*hKQWQt8r7B#lYX8xoSHFGCW-*DSQ4EM4M3Mw%gkSYNK18@(e zfzMF}WWaCyS@1y%-~Xg0ry~tkQkUmKuI5lGAua{{vn22V!2T()AU5FpKh@Nv)s^Js zv~@Vu<dG2$ssIa;-wW`<?Pob4z7KpqNIm(x8bBn6f7NLGS;Ojk%$46(Bs#1II-vS^ zyy8DgWk^a2ogemK!2*Fy$UvYA{{VnMupk;>UG;=CnLmQR{PeUBQf2;lAV!vG>^Z0N zL88rrjL-*J!43;7C=w9xhcw`yjRKq7o4L9=0SmR9PA-nX12@#h(iIu-0N_xm2OV)( zU_raT0y>$wm^oMi2|U3N;OhF9uy}`<-xVka#DV*l{O0yHzi9vUxa1Qtpi$buR*8cU zd4~lS1pT$L^!0=6qUKOpM+XPsy{f7W#1bjrEwaeN!Ik9(zySIT^pEHvHgJUneFN4) zk=k|$55(g8slmS|@+*4fr2urd3LwjIIZA**g+%l(SZNn4HwQ}y6o`vw>2&mR1X+&q zDa1Af0B;4rAMZMOlHbAqK|R_xuwJ7ANARtFE({-P2o{tJJR<>2KVp)ZK-M;)ejx zd*E~Mka<{OL7%CAhk4n|1qg?97-I!l0rOinjVi#arbgg4bi5;nY5oFL`UWtP<!xMC zq1tZOf2#jvtAo2;dyoxinHg9wKd`*R0t@mv_qRkp)Z=<G!5Q|(^Lv0KZh*~+9ijtQ zSP<m=Ul7Px-f(mQq9^`^C`%4Yga_mC3t#~9$C%oHj`{E2{n-<;X0Db%@C8eVs|^$g z*r*MpnTA*ax;wZt{PSu6xu3-HuvM@C)p-(tK;p+Zq3nObsR9A=9R5(>k5&L#grSxv zE3!}=1px!ZTLT90aYc^s`~{VojjJml&<`@e41dFP+XU6D0AOkbn2rlI3>^LcqauG& zc$m3Z{!u8LvUrm^fT{qX5<I5AabS1OUsC<4lTtYvXYzo%Ne(a!5BB^V7QjRS+xknA zKZ+vE!SeYLAW9W*Yd>yD9{?r(CCiUdck%!T`KIZd2oQJz1joB&M(Teg_>;yS<2<KE z2dLnHDFK7)p8^XSko^m)Kk8~M@mtUYfNuww&Vko-SYSb{faU&CSGo|p|G{vww;L8s z2|=I_z)Zq?$OK$rLD!Z3Om=c#P~Lej(Frsj1mGUWL^t{c^Se4Me%^);X7Q6Ty{6Ei zqkvN6fd1t;)=ol;KV$x|x|5NO+@H(%0tSE$7=XwzWC5#RkzE{ZEzP0-AFlwbM@amD zXBUt{_!tkC%`ZI2OUM7x&mX4o17v{Vd%^#C1%3CxCTx$<xIt~~e{sPMDje1ZqM7_G z2M#c<-LJK6AizutG5ZyU?iGV-9iY!};Ldg2+~t1@1Nf{uE@tkQF0N+w--G-du9hQD zE%M|^h2lU%&j2<kao9}Y3JcP5{7pN5`q}^9v8d}}{|AjCC%ZqSg9UwZKE`$UP$1*z z2t9C4oeunYU@CC|wDe!T3~~zfBk&d1zXm^fU?XP|K7y9_IuZIXhTng+6*+J35g?oQ z?*acRi!X8?Bd6v(qO0`(Jsn`4CnoAdW<X8`c*OAV=5HBJRycBq?;|+c<ln*p?L8r@ zF>-5>BWfSPpG`Rt{!j6>kqMAvl^zk0JUEfy$HVJMkxP-GkwZuxL62me2#pj_5*ZIU zP~#C^OZLfl$HO)v;~~c&JHivn|1I9H5y_CDkt0JLLGKm(4*KLVhJ2jh2#vJuM6`b& zE<kP?@_z3lu;%s?!H(?={;%EN$SlY^j*nP!JO9jbvKo+gUmamC_MV7|JfR-ji-p`` z<h=|>==-lvME^Oj022xF&IV*?<Ym_*=qDq;gFe0pdszh?m{|`Tb|Fw25ePIfbMVvu E0aA=+Q2+n{ literal 0 HcmV?d00001 diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..0d23ac00c --- /dev/null +++ b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/powertools-examples-core/kotlin/gradlew b/examples/powertools-examples-core/kotlin/gradlew new file mode 100755 index 000000000..fcb6fca14 --- /dev/null +++ b/examples/powertools-examples-core/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/kotlin/gradlew.bat b/examples/powertools-examples-core/kotlin/gradlew.bat new file mode 100644 index 000000000..6689b85be --- /dev/null +++ b/examples/powertools-examples-core/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/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt new file mode 100644 index 000000000..ed4cf267a --- /dev/null +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt @@ -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 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.apache.logging.log4j.LogManager +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.logging.Logging +import software.amazon.lambda.powertools.logging.LoggingUtils +import software.amazon.lambda.powertools.metrics.Metrics +import software.amazon.lambda.powertools.metrics.MetricsUtils +import software.amazon.lambda.powertools.tracing.CaptureMode +import software.amazon.lambda.powertools.tracing.Tracing +import software.amazon.lambda.powertools.tracing.TracingUtils +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.URL +import java.util.stream.Collectors + +/** + * Handler for requests to Lambda function. + */ + +class App : RequestHandler<APIGatewayProxyRequestEvent?, APIGatewayProxyResponseEvent> { + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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") + MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) + MetricsUtils.withSingleMetric("CustomMetrics2", 1.0, Unit.COUNT, "Another") { metric: MetricsLogger -> + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")) + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")) + } + LoggingUtils.appendKey("test", "willBeLogged") + val response = APIGatewayProxyResponseEvent().withHeaders(headers) + return try { + val pageContents = getPageContents("https://checkip.amazonaws.com") + log.info(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() + } + } + + private val log = LogManager.getLogger(App::class) +} diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt new file mode 100644 index 000000000..99f6bbfa0 --- /dev/null +++ b/examples/powertools-examples-core/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) + @Metrics(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/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt new file mode 100644 index 000000000..8aae081e8 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt @@ -0,0 +1,20 @@ +package helloworld + + +import org.junit.Assert +import org.junit.Test + +class AppTest { + @Test + fun successfulResponse() { + val app = App() + val result = app.handleRequest(null, null) + Assert.assertEquals(200, result.statusCode.toLong()) + Assert.assertEquals("application/json", result.headers["Content-Type"]) + val content = result.body + Assert.assertNotNull(content) + Assert.assertTrue(""""message"""" in content) + Assert.assertTrue(""""hello world"""" in content) + Assert.assertTrue(""""location"""" in content) + } +} diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml new file mode 100644 index 000000000..1a1572fca --- /dev/null +++ b/examples/powertools-examples-core/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/serverless/README.md b/examples/powertools-examples-core/serverless/README.md index aec093182..9e33aa9ff 100644 --- a/examples/powertools-examples-core/serverless/README.md +++ b/examples/powertools-examples-core/serverless/README.md @@ -5,7 +5,7 @@ For general information on the deployed example itself, you can refer to the par 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. +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. From 228c0d8f3b66613d19a59f18b436de9907668ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 24 Oct 2023 12:15:46 +0200 Subject: [PATCH 0513/1008] chore: java21 support in our build (#1488) --- .github/workflows/pr_build.yml | 2 +- examples/powertools-examples-batch/pom.xml | 1 + .../pom.xml | 1 + .../powertools-examples-core/cdk/app/pom.xml | 8 +- .../app/src/test/java/helloworld/AppTest.java | 59 ---------- .../gradle/build.gradle | 1 - .../src/test/java/helloworld/AppTest.java | 24 ---- examples/powertools-examples-core/sam/pom.xml | 8 +- .../sam/src/test/java/helloworld/AppTest.java | 59 ---------- .../serverless/pom.xml | 8 +- .../src/test/java/helloworld/AppTest.java | 59 ---------- .../terraform/pom.xml | 8 +- .../src/test/java/helloworld/AppTest.java | 38 ------- .../powertools-examples-idempotency/pom.xml | 25 +---- .../src/test/java/helloworld/AppTest.java | 105 ------------------ .../src/test/resources/event.json | 63 ----------- .../powertools-examples-parameters/pom.xml | 20 ---- .../powertools-examples-serialization/pom.xml | 26 ----- ...wayRequestDeserializationFunctionTest.java | 49 -------- .../SQSEventDeserializationFunctionTest.java | 59 ---------- examples/powertools-examples-sqs/pom.xml | 1 + .../powertools-examples-validation/pom.xml | 26 ----- .../validation/InboundValidationTest.java | 65 ----------- pom.xml | 74 ++++++++++-- powertools-batch/pom.xml | 10 -- powertools-cloudformation/pom.xml | 5 - powertools-core/pom.xml | 11 -- powertools-idempotency/pom.xml | 10 -- powertools-large-messages/pom.xml | 10 -- powertools-logging/pom.xml | 10 -- powertools-metrics/pom.xml | 10 -- powertools-parameters/pom.xml | 10 -- powertools-sqs/pom.xml | 5 - powertools-test-suite/pom.xml | 5 - powertools-tracing/pom.xml | 10 -- powertools-validation/pom.xml | 5 - 36 files changed, 71 insertions(+), 819 deletions(-) delete mode 100644 examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java delete mode 100644 examples/powertools-examples-idempotency/src/test/resources/event.json delete mode 100644 examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java delete mode 100644 examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java delete mode 100644 examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index e99900ce5..04f35c63b 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -48,7 +48,7 @@ jobs: strategy: max-parallel: 5 matrix: - java: [8, 11, 15, 16, 17, 18, 19, 20 ] + java: [8, 11, 15, 16, 17, 18, 19, 20, 21 ] name: Java ${{ matrix.java }} env: JAVA: ${{ matrix.java }} diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index bbdc19b9f..99d3a97cc 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -105,6 +105,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 01aedcaaf..c195cd262 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -130,6 +130,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index 57397f187..b4652bd65 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -51,13 +51,6 @@ <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> @@ -105,6 +98,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> 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/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle index e7367c246..06aec59e8 100644 --- a/examples/powertools-examples-core/gradle/build.gradle +++ b/examples/powertools-examples-core/gradle/build.gradle @@ -30,6 +30,5 @@ dependencies { aspect 'software.amazon.lambda:powertools-tracing:1.17.0' aspect 'software.amazon.lambda:powertools-logging:1.17.0' aspect 'software.amazon.lambda:powertools-metrics:1.17.0' - testImplementation 'junit:junit:4.13.2' } diff --git a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java deleted file mode 100644 index af3ec1275..000000000 --- a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package helloworld; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class AppTest { - @Test - public void successfulResponse() { - App app = new App(); - APIGatewayProxyResponseEvent result = app.handleRequest(null, null); - assertEquals(200, result.getStatusCode().intValue()); - assertEquals("application/json", result.getHeaders().get("Content-Type")); - 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/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 93c858556..997472344 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -51,13 +51,6 @@ <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> @@ -104,6 +97,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> 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-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index faa47c591..6fce1d2dd 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -51,13 +51,6 @@ <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> @@ -105,6 +98,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> diff --git a/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 70dad8d71..000000000 --- a/examples/powertools-examples-core/serverless/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/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 8da2780d8..2a55c31bc 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -51,13 +51,6 @@ <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> @@ -105,6 +98,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> diff --git a/examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.java deleted file mode 100644 index 0ca4f264d..000000000 --- a/examples/powertools-examples-core/terraform/src/test/java/helloworld/AppTest.java +++ /dev/null @@ -1,38 +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 org.junit.Test; - -public class AppTest { - - @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 index a98645a29..8cda4bfb3 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -64,30 +64,6 @@ <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> @@ -167,6 +143,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> 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-idempotency/src/test/resources/event.json b/examples/powertools-examples-idempotency/src/test/resources/event.json deleted file mode 100644 index fd7f5ace7..000000000 --- a/examples/powertools-examples-idempotency/src/test/resources/event.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "body": "{\"address\": \"https://checkip.amazonaws.com\"}", - "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-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index deb9c6ba6..0e07b0cbb 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -33,26 +33,6 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.3</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> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index eee3e456a..495fe8b77 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -33,36 +33,10 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.3</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> <!-- Don't deploy the example --> <plugin> <groupId>org.apache.maven.plugins</groupId> 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/pom.xml b/examples/powertools-examples-sqs/pom.xml index 889fe85da..0338fe7d9 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -103,6 +103,7 @@ <goal>shade</goal> </goals> <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 29f52b33e..65628f6b2 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -42,36 +42,10 @@ <artifactId>aws-lambda-java-core</artifactId> <version>1.2.3</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> <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> 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/pom.xml b/pom.xml index 823b09d61..e1a0c5a6c 100644 --- a/pom.xml +++ b/pom.xml @@ -263,18 +263,6 @@ <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> - <scope>test</scope> - </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> @@ -286,6 +274,12 @@ <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> + <exclusions> + <exclusion> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.skyscreamer</groupId> @@ -544,6 +538,62 @@ </plugins> </build> </profile> + <profile> + <id>olderThanJdk11</id> + <activation> + <jdk>(,11)</jdk> + </activation> + <properties> + <!-- mockito 5+ is not compatible anymore with java < 11 --> + <mockito.version>4.11.0</mockito.version> + </properties> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-inline</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + </profile> + <profile> + <id>newerThanJdk11</id> + <activation> + <jdk>[11,)</jdk> + </activation> + <properties> + <mockito.version>5.6.0</mockito.version> + </properties> + <dependencies> + <!-- since mockito 5.3, no need to have mockito-inline --> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <systemPropertyVariables> + <!-- TODO: remove when updating mockito / bytebuddy with Java21 compat --> + <net.bytebuddy.experimental>true</net.bytebuddy.experimental> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + </build> + </profile> <profile> <id>newerThanJdk8</id> <activation> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 27bb33a42..0c59ce497 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -56,16 +56,6 @@ <artifactId>aws-lambda-java-tests</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> </dependencies> </project> \ No newline at end of file diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 1347d95a3..f6cd822a6 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -97,11 +97,6 @@ <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index a5460031d..b848e9f79 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -86,11 +86,6 @@ <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> @@ -101,11 +96,6 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <scope>test</scope> - </dependency> </dependencies> <build> @@ -122,5 +112,4 @@ </plugin> </plugins> </build> - </project> \ No newline at end of file diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index e89df47f7..9dfde1bd1 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -107,16 +107,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> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index aed8cc2e4..8fdae2139 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -118,16 +118,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> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 26ad8c243..c31448e30 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -104,16 +104,6 @@ <artifactId>commons-lang3</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.aspectj</groupId> <artifactId>aspectjweaver</artifactId> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index cb3acf110..eff7296c8 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -104,16 +104,6 @@ <artifactId>commons-lang3</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.aspectj</groupId> <artifactId>aspectjweaver</artifactId> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index f99a3d3cd..808232ab1 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -120,16 +120,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> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index f824f409d..c4b2b54f0 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -115,11 +115,6 @@ <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> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 55e713ec0..34e80b06e 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -100,11 +100,6 @@ <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> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d2f14c3e0..d98a97e2a 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -105,16 +105,6 @@ <artifactId>commons-lang3</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.aspectj</groupId> <artifactId>aspectjweaver</artifactId> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 606f02139..24de0ba2a 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -111,11 +111,6 @@ <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> From 5f26c7683eb1b5ec182837f3b14df8fad2886645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:28:00 +0200 Subject: [PATCH 0514/1008] chore: add missing projects and improve workflow (#1487) --- .github/workflows/pr_build.yml | 53 ++++++++++++++++------------- .github/workflows/run-e2e-tests.yml | 12 ++++--- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 04f35c63b..c316c0073 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -6,18 +6,21 @@ on: - main - v2 paths: + - 'powertools-batch/**' - 'powertools-cloudformation/**' - - 'powertools-core/**' - - 'powertools-serialization/**' + - 'powertools-core/**' # not in v2 + - 'powertools-common/**' # v2 only + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' - 'powertools-logging/**' - - 'powertools-sqs/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' # not in v2 + - 'powertools-test-suite/**' # not in v2 - 'powertools-tracing/**' - 'powertools-validation/**' - - 'powertools-idempotency/**' - - 'powertools-parameters/**' - - 'powertools-metrics/**' - - 'powertools-test-suite/**' - - 'powertools-e2e-tests/**' - 'examples/**' - 'pom.xml' - 'examples/pom.xml' @@ -26,18 +29,20 @@ on: branches: - main paths: + - 'powertools-batch/**' - 'powertools-cloudformation/**' - 'powertools-core/**' - - 'powertools-serialization/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' - 'powertools-sqs/**' + - 'powertools-test-suite/**' - 'powertools-tracing/**' - 'powertools-validation/**' - - 'powertools-idempotency/**' - - 'powertools-parameters/**' - - 'powertools-metrics/**' - - 'powertools-test-suite/**' - - 'powertools-e2e-tests/**' - 'examples/**' - 'pom.xml' - 'examples/pom.xml' @@ -48,7 +53,7 @@ jobs: strategy: max-parallel: 5 matrix: - java: [8, 11, 15, 16, 17, 18, 19, 20, 21 ] + java: [8, 11, 17, 21, 15, 16, 18, 19, 20] name: Java ${{ matrix.java }} env: JAVA: ${{ matrix.java }} @@ -68,26 +73,26 @@ jobs: run: mvn -B install --file pom.xml - name: Build Gradle Example - Java if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 - run: | - cd examples/powertools-examples-core/gradle - ./gradlew build + working-directory: examples/powertools-examples-core/gradle + run: ./gradlew build - name: Build Gradle Example - Kotlin - run: | - cd examples/powertools-examples-core/kotlin - ./gradlew build + if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 + working-directory: examples/powertools-examples-core/kotlin + run: ./gradlew build - name: Setup Terraform if: ${{ matrix.java == '11' }} uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - name: Setup AWS credentials + if: ${{ matrix.java == '11' }} uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_REGION }} - name: Terraform validate + working-directory: examples/powertools-examples-core/terraform if: ${{ matrix.java == '11' }} run: | terraform -version - cd examples/powertools-examples-core/terraform terraform init -backend=false terraform validate terraform plan @@ -95,17 +100,17 @@ jobs: if: ${{ matrix.java == '11' }} uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - name: Terraform lint + working-directory: examples/powertools-examples-core/terraform if: ${{ matrix.java == '11' }} run: | tflint --version - cd examples/powertools-examples-core/terraform tflint --init tflint -f compact - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.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 + 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,./powertools-large-messages/target/site/jacoco/jacoco.xml,./powertools-batch/target/site/jacoco/jacoco.xml savepr: runs-on: ubuntu-latest name: Save PR number if running on PR by dependabot diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index c4a8c6fb2..a2a1b9aec 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -9,14 +9,16 @@ on: - v2 paths: # add other modules when there are under e2e tests - 'powertools-e2e-tests/**' + - 'powertools-batch/**' - 'powertools-core/**' - - 'powertools-serialization/**' - - 'powertools-logging/**' - - 'powertools-tracing/**' + - 'powertools-common/**' - 'powertools-idempotency/**' - - 'powertools-parameters/**' + - 'powertools-large-message/**' + - 'powertools-logging/**' - 'powertools-metrics/**' - - 'powertools-large-messages/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-tracing/**' - 'pom.xml' - '.github/workflows/**' From cd0f3dc1b00f5c4a34138febe9e7dc0cae9ea4f9 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 24 Oct 2023 16:18:23 +0200 Subject: [PATCH 0515/1008] docs(customer-reference): add Vertex Pharmaceuticals as a customer reference (#1486) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2afb40e5d..900395554 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,7 @@ 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/) +* [Vertex Pharmaceuticals](https://www.vrtx.com/) ## Credits From a3d2c9b7d0b3b1bbd430d73bd2d5c8a50f036452 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Tue, 24 Oct 2023 16:41:44 +0200 Subject: [PATCH 0516/1008] Merge cleanup: fix some missing changes --- .github/workflows/pr_build.yml | 2 +- examples/README.md | 10 +++++----- .../kotlin/build.gradle.kts | 6 +++--- .../terraform/pom.xml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 0f749a4a0..b22dd80bc 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -73,7 +73,7 @@ jobs: run: ./gradlew build - name: Build Gradle Example - Kotlin if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 - working-directory: examples/powertools-examples-core/kotlin + working-directory: examples/powertools-examples-core-utilities/kotlin run: ./gradlew build - name: Setup Terraform if: ${{ matrix.java == '11' }} diff --git a/examples/README.md b/examples/README.md index 6dbe00185..48df52df8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,11 +6,11 @@ Each example can be copied from its subdirectory and used independently of the r ## Examples * [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools and languages - * [CDK](./powertools-examples-core/cdk) - * [Gradle](./powertools-examples-core/gradle) - * [SAM](./powertools-examples-core/sam) - * [Serverless](./powertools-examples-core/serverless) - * [Kotlin](./powertools-examples-core/kotlin) + * [CDK](./powertools-examples-core-utilities/cdk) + * [Gradle](./powertools-examples-core-utilities/gradle) + * [SAM](./powertools-examples-core-utilities/sam) + * [Serverless](./powertools-examples-core-utilities/serverless) + * [Kotlin](./powertools-examples-core-utilities/kotlin) * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index fc363c1b9..29a29b0c4 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -14,9 +14,9 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:2.13.2.2") implementation("com.amazonaws:aws-lambda-java-events:3.11.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2") - aspect("software.amazon.lambda:powertools-tracing:1.18.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-logging:1.18.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-metrics:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-tracing:2.0.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-logging:2.0.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT") testImplementation("junit:junit:4.13.2") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 2a55c31bc..538a6da8f 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-terraform</artifactId> <packaging>jar</packaging> From 327846f699f5a1bf2df12791dd6414b8dbbc6853 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Tue, 24 Oct 2023 16:55:58 +0200 Subject: [PATCH 0517/1008] Merge fixes - readme link and terraform build --- examples/README.md | 2 +- examples/pom.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 48df52df8..aa8457def 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,7 @@ Each example can be copied from its subdirectory and used independently of the r ## Examples -* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools and languages +* [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) * [Gradle](./powertools-examples-core-utilities/gradle) * [SAM](./powertools-examples-core-utilities/sam) diff --git a/examples/pom.xml b/examples/pom.xml index be52778da..3526be2a4 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -33,6 +33,7 @@ <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</module> <module>powertools-examples-parameters</module> <module>powertools-examples-serialization</module> From cfe069f61161c26e85d570c7b15a1d44b5949c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 24 Oct 2023 17:18:04 +0200 Subject: [PATCH 0518/1008] enforce our version of jackson-databind in serialization module (#1472) --- powertools-serialization/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index b1f228df7..e5c4087fe 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -67,6 +67,10 @@ <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> <!-- Test dependencies --> <dependency> From 6e334199976f004e0d4ab115e1bc1eb35fd13a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 26 Oct 2023 11:12:43 +0200 Subject: [PATCH 0519/1008] chore: artifacts size on good branches (#1493) --- .github/workflows/pr_artifacts_size.yml | 23 ++++++++++++------- .../powertools-examples-idempotency/pom.xml | 7 ------ powertools-idempotency/pom.xml | 7 ------ 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml index cbacd78da..f37f83a8d 100644 --- a/.github/workflows/pr_artifacts_size.yml +++ b/.github/workflows/pr_artifacts_size.yml @@ -3,19 +3,26 @@ name: Artifacts Size on: pull_request: branches: - - master + - main + - v2 paths: + - 'powertools-batch/**' - 'powertools-cloudformation/**' - - 'powertools-core/**' - - 'powertools-serialization/**' + - 'powertools-core/**' # not in v2 + - 'powertools-common/**' # v2 only + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' - 'powertools-logging/**' - - 'powertools-sqs/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' # not in v2 + - 'powertools-test-suite/**' # not in v2 - 'powertools-tracing/**' - 'powertools-validation/**' - - 'powertools-parameters/**' - - 'powertools-idempotency/**' - - 'powertools-metrics/**' - 'pom.xml' + - '.github/workflows/pr_artifacts_size.yml' jobs: codecheck: runs-on: ubuntu-latest @@ -27,7 +34,7 @@ jobs: distribution: 'corretto' java-version: 11 - name: Build with Maven - run: mvn clean package --file pom.xml -DskipTests + run: mvn clean package --file pom.xml -DskipTests artifact:buildinfo - name: Get artifacts size & build report id: artifacts-size-report run: | diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 8cda4bfb3..4b8eba9c9 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -244,11 +244,4 @@ </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/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 9dfde1bd1..8b3459dee 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -189,11 +189,4 @@ </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 From 62ccaf94fb23cd0843f35e922b4c57a2d7267965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 26 Oct 2023 22:26:29 +0200 Subject: [PATCH 0520/1008] chore(v2): clean examples (#1495) --- .../cdk/app/pom.xml | 9 +- .../sam/pom.xml | 138 +++++++++--------- .../serverless/pom.xml | 9 +- .../terraform/pom.xml | 79 +++------- 4 files changed, 86 insertions(+), 149 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 2d8e3b371..be8c9ca67 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -2,13 +2,13 @@ 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) library 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>1.17.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <properties> <log4j.version>2.20.0</log4j.version> @@ -58,13 +58,6 @@ <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> - </dependency> </dependencies> <build> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 130fb814d..a88150f4b 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -56,81 +56,75 @@ <artifactId>aspectjrt</artifactId> <version>${aspectj.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> - <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> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - <configuration> - <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.1.0</version> - </dependency> - </dependencies> - </plugin> - </plugins> + <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> + <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> + <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.1.0</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 diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index ce0f6f081..c92d0b653 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -2,7 +2,7 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with SAM</name> + <name>Powertools for AWS Lambda (Java) library Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> @@ -56,13 +56,6 @@ <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.13.2</version> - <scope>test</scope> - </dependency> </dependencies> <build> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 538a6da8f..d74ce7085 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -2,17 +2,17 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> - <artifactId>powertools-examples-core-terraform</artifactId> + <artifactId>powertools-examples-core-utilities-terraform</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> + <aspectj.version>1.9.20</aspectj.version> </properties> <dependencies> @@ -34,12 +34,12 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.2</version> + <version>1.2.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> @@ -51,6 +51,11 @@ <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> </dependencies> <build> @@ -86,6 +91,13 @@ </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> @@ -100,7 +112,7 @@ <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> - <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> </transformers> </configuration> </execution> @@ -151,61 +163,6 @@ <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> From 0a30d8e9e6cfa117e998509883233ebf411263c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:06:35 +0100 Subject: [PATCH 0521/1008] Update CONTRIBUTING.md --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8db303737..c4330cf8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,8 +55,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) From 7918d9a862cdaa30dbb93b51232216e31f79f5c3 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi <mriccia@amazon.com> Date: Wed, 8 Nov 2023 16:35:51 +0100 Subject: [PATCH 0522/1008] fix: get trace id from system property when env var is not set (#1503) * fix: check if XRAY Trace ID is present in System property * chore: remove erroneous extra char in tests --- .../core/internal/LambdaConstants.java | 1 + .../core/internal/LambdaHandlerProcessor.java | 8 +++- .../core/internal/SystemWrapper.java | 4 ++ .../amazon/lambda/powertools/LoggingE2ET.java | 2 + .../internal/LambdaLoggingAspectTest.java | 21 ++++++++- .../powertools/metrics/MetricsLoggerTest.java | 44 +++++++++++++++++-- .../internal/LambdaMetricsAspectTest.java | 6 +-- 7 files changed, 77 insertions(+), 9 deletions(-) diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java index d0f94260b..e64e334c5 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java @@ -24,6 +24,7 @@ public class LambdaConstants { @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_SAM_LOCAL = "AWS_SAM_LOCAL"; public static final String ROOT_EQUALS = "Root="; public static final String POWERTOOLS_SERVICE_NAME = "POWERTOOLS_SERVICE_NAME"; diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index e9e220e41..91c93c3f3 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -16,6 +16,7 @@ import static java.util.Optional.empty; import static java.util.Optional.of; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getProperty; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; @@ -93,7 +94,12 @@ public static boolean isSamLocal() { } public static Optional<String> getXrayTraceId() { - final String X_AMZN_TRACE_ID = getenv(LambdaConstants.X_AMZN_TRACE_ID); + String X_AMZN_TRACE_ID = getenv(LambdaConstants.X_AMZN_TRACE_ID); + // For the Java Lambda 17+ runtime, the Trace ID is set as a System Property + if (X_AMZN_TRACE_ID == null) { + X_AMZN_TRACE_ID = getProperty(LambdaConstants.XRAY_TRACE_HEADER); + } + if (X_AMZN_TRACE_ID != null) { return of(X_AMZN_TRACE_ID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")); } diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java index 30f72232f..99046ffec 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java @@ -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-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..b060879d3 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 @@ -46,6 +46,7 @@ public class LoggingE2ET { public static void setup() { infrastructure = Infrastructure.builder() .testName(LoggingE2ET.class.getSimpleName()) + .tracing(true) .pathToFunction("logging") .environmentVariables( Stream.of(new String[][] { @@ -83,6 +84,7 @@ public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { assertThat(jsonNode.get("message").asText()).isEqualTo("New Order"); assertThat(jsonNode.get("orderId").asText()).isEqualTo(orderId); assertThat(jsonNode.get("coldStart").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 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..6952fe755 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 @@ -24,6 +24,7 @@ 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.getProperty; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; @@ -245,13 +246,31 @@ void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { .containsEntry("service", "testService"); } + @Test + void shouldLogxRayTraceIdSystemPropertySet() { + String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; + + try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { + mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn(null); + mocked.when(() -> getProperty("com.amazonaws.xray.traceHeader")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); + + requestHandler.handleRequest(new Object(), context); + + assertThat(ThreadContext.getImmutableContext()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry("xray_trace_id", xRayTraceId); + } + } + @Test void shouldLogxRayTraceIdEnvVarSet() { String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); requestHandler.handleRequest(new Object(), context); 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 index 7f234a4d6..16a068849 100644 --- 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 @@ -18,6 +18,7 @@ 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.getProperty; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.fasterxml.jackson.core.JsonProcessingException; @@ -66,7 +67,7 @@ void singleMetricsCaptureUtilityWithDefaultDimension() { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.defaultDimensions(DimensionSet.of("Service", "Booking")); @@ -96,7 +97,7 @@ void singleMetricsCaptureUtility() { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, "test", metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); @@ -123,7 +124,7 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); @@ -165,6 +166,41 @@ void shouldThrowExceptionWhenDefaultDimensionIsNull() { .withMessage("Null dimension set not allowed"); } + @Test + void shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent() { + 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(null); + internalWrapper.when(() -> getProperty("com.amazonaws.xray.traceHeader")) + .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"); + }); + } + } + private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic( @@ -172,7 +208,7 @@ private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); methodToTest.accept(metricsLogger -> { 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..0a735c75e 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 @@ -91,7 +91,7 @@ public void metricsWithoutColdStart() { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.defaultDimensions(null); requestHandler = new PowertoolsMetricsEnabledHandler(); @@ -135,7 +135,7 @@ public void metricsWithDefaultDimensionSpecified() { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); requestHandler = new PowertoolsMetricsEnabledDefaultDimensionHandler(); @@ -179,7 +179,7 @@ public void metricsWithDefaultNoDimensionSpecified() { 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\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); requestHandler = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); From 3afb1d2e5a548eb3fea3bbe4728fa3708ec61106 Mon Sep 17 00:00:00 2001 From: jdoherty <jdoherty07@gmail.com> Date: Wed, 8 Nov 2023 16:40:20 +0000 Subject: [PATCH 0523/1008] removing dynamodb local from examples (#1507) --- examples/powertools-examples-idempotency/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 99eb5dc9f..901074ddb 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -83,12 +83,6 @@ <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> From 0f3e8960d5fab2f658e5e855e7bd979c9d77d17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:52:08 +0100 Subject: [PATCH 0524/1008] fix #1500 (#1506) --- .../powertools/metrics/MetricsUtils.java | 8 +++-- .../powertools/metrics/MetricsLoggerTest.java | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) 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 index 09517d46e..7891b22ec 100644 --- 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 @@ -178,8 +178,12 @@ private static void captureRequestAndTraceId(MetricsLogger metricsLogger) { private static String defaultNameSpace() { MetricsContext context = MetricsLoggerHelper.metricsContext(); - return "aws-embedded-metrics".equals(context.getNamespace()) ? - SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE") : context.getNamespace(); + if ("aws-embedded-metrics".equals(context.getNamespace())) { + String namespace = SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE"); + return namespace != null ? namespace : "aws-embedded-metrics"; + } else { + return context.getNamespace(); + } } private static Optional<String> awsRequestId() { 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 index 16a068849..ce9d63cfd 100644 --- 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 @@ -112,6 +112,37 @@ void singleMetricsCaptureUtility() { .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=test"); + }); + } + } + + @Test + void singleMetricsCaptureUtilityWithNullNamespace() { + 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"); + // POWERTOOLS_METRICS_NAMESPACE is not defined + + MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, + metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); + + assertThat(out.toString()) + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=aws-embedded-metrics"); }); } } From de547d02754216c06812d73d80159d55be8007e3 Mon Sep 17 00:00:00 2001 From: Alexey Soshin <alexey.soshin@gmail.com> Date: Tue, 14 Nov 2023 14:27:38 +0000 Subject: [PATCH 0525/1008] feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) --- docs/core/logging.md | 4 +- docs/index.md | 2 + .../core/internal/LambdaHandlerProcessor.java | 7 ++ .../logging/internal/LambdaLoggingAspect.java | 14 ++- .../handlers/PowerToolLogEventDisabled.java | 28 +++++ .../internal/LambdaLoggingAspectTest.java | 119 +++++++++++------- 6 files changed, 127 insertions(+), 47 deletions(-) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java diff --git a/docs/core/logging.md b/docs/core/logging.md index 2df9a4529..fd74db3d2 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -217,9 +217,7 @@ Key | Type | Example | Description ## Capturing context Lambda info -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 debugging in non-production environments, you can instruct Logger to log the incoming event with `@Logger(logEvent = true)` or via `POWERTOOLS_LOGGER_LOG_EVENT=true` environment variable. !!! warning Log event is disabled by default to prevent sensitive info being logged. diff --git a/docs/index.md b/docs/index.md index 6af4c5e8d..92589be7c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -285,5 +285,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl | **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_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/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index 91c93c3f3..d4e18dddc 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -47,6 +47,13 @@ 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 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..ff556d89a 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 @@ -33,6 +33,7 @@ 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; @@ -42,6 +43,7 @@ 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; @@ -65,6 +67,7 @@ public final class LambdaLoggingAspect { 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 Boolean LOG_EVENT; private static Level LEVEL_AT_INITIALISATION; @@ -74,6 +77,13 @@ public final class LambdaLoggingAspect { } LEVEL_AT_INITIALISATION = LOG.getLevel(); + + String logEvent = System.getenv("POWERTOOLS_LOGGER_LOG_EVENT"); + if (logEvent != null) { + LOG_EVENT = Boolean.parseBoolean(logEvent); + } else { + LOG_EVENT = false; + } } private static void resetLogLevels(Level logLevel) { @@ -104,7 +114,9 @@ public Object around(ProceedingJoinPoint pjp, getXrayTraceId().ifPresent(xRayTraceId -> appendKey("xray_trace_id", xRayTraceId)); - if (logging.logEvent()) { + // Check that the environment variable was enabled explicitly + // Or that the handler was annotated with @Logging(logEvent = true) + if (LOG_EVENT || logging.logEvent()) { proceedArgs = logEvent(pjp); } diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java new file mode 100644 index 000000000..77103e450 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.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 PowerToolLogEventDisabled implements RequestHandler<Object, Object> { + + @Logging(logEvent = false) + @Override + public Object handleRequest(Object input, Context context) { + return null; + } +} 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 6952fe755..75cf22aa5 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,19 +14,6 @@ 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.getProperty; -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; @@ -37,24 +24,10 @@ 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.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.Paths; -import java.nio.file.StandardOpenOption; -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 org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -62,18 +35,33 @@ 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 software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledWithClearState; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.*; + +import java.io.*; +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.List; +import java.util.Map; +import java.util.stream.Collectors; + +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.junit.jupiter.api.Assertions.assertTrue; +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.getProperty; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; class LambdaLoggingAspectTest { @@ -116,7 +104,7 @@ void shouldSetLambdaContextWhenEnabled() { void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { requestStreamHandler = new PowerLogToolEnabledForStream(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); assertThat(ThreadContext.getImmutableContext()) @@ -132,14 +120,14 @@ void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { @Test void shouldSetColdStartFlag() throws IOException { - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) .containsEntry("coldStart", "true"); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); assertThat(ThreadContext.getImmutableContext()) @@ -195,6 +183,51 @@ void shouldLogEventForHandler() throws IOException, JSONException { assertEquals(expectEvent, event, false); } + /** + * If POWERTOOLS_LOGGER_LOG_EVENT was set to true, the handler should log, despite @Logging(logEvent=false) + * + * @throws IOException + */ + @Test + void shouldLogEventForHandlerWhenEnvVariableSetToTrue() throws IOException, IllegalAccessException, JSONException { + try { + writeStaticField(LambdaLoggingAspect.class, "LOG_EVENT", Boolean.TRUE, true); + + requestHandler = new PowerToolLogEventDisabled(); + S3EventNotification s3EventNotification = s3EventNotification(); + + requestHandler.handleRequest(s3EventNotification, context); + + Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + + String event = (String) log.get("message"); + + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) + .lines().collect(joining("\n")); + + assertEquals(expectEvent, event, false); + } finally { + writeStaticField(LambdaLoggingAspect.class, "LOG_EVENT", Boolean.FALSE, true); + } + } + + /** + * If POWERTOOLS_LOGGER_LOG_EVENT was set to false and @Logging(logEvent=false), the handler shouldn't log + * + * @throws IOException + */ + @Test + void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() throws IOException { + requestHandler = new PowerToolLogEventDisabled(); + S3EventNotification s3EventNotification = s3EventNotification(); + + requestHandler.handleRequest(s3EventNotification, context); + + Assertions.assertEquals(0, + Files.lines(Paths.get("target/logfile.json")).collect(joining()).length()); + } + @Test void shouldLogEventForHandlerWithOverriddenObjectMapper() throws IOException, JSONException { RequestHandler<S3EventNotification, Object> handler = new PowerToolLogEventEnabledWithCustomMapper(); From 30c8d33d0d663ec0d6512cfbcea49e2e68aff0c9 Mon Sep 17 00:00:00 2001 From: jdoherty <jdoherty07@gmail.com> Date: Tue, 14 Nov 2023 14:29:35 +0000 Subject: [PATCH 0526/1008] chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) --- .../powertools/tracing/TracingUtils.java | 21 +++++++++++++++++++ .../powertools/tracing/TracingUtilsTest.java | 21 +++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) 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..47c4e7422 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 @@ -21,12 +21,15 @@ 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/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java index 78283fbc2..01f25f37a 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 @@ -28,7 +28,6 @@ import org.junit.jupiter.api.Test; class TracingUtilsTest { - @BeforeEach void setUp() { AWSXRay.beginSegment("test"); @@ -123,6 +122,24 @@ void shouldInvokeCodeBlockWrappedWithinSubsegment() { }); } + @Test + void shouldNotAddAnnotationIfInvalidCharacterInKey() { + AWSXRay.beginSubsegment("subSegment"); + String inputKey = "stringKey with spaces"; + TracingUtils.putAnnotation(inputKey, "val"); + AWSXRay.getCurrentSubsegmentOptional() + .ifPresent(segment -> assertThat(segment.getAnnotations()).size().isEqualTo(0)); + } + + @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() { Context test = mock(Context.class); @@ -221,4 +238,4 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter .containsEntry("key", "val"); }); } -} \ No newline at end of file +} From 6b8fe49dddf33c8d63b1346a365b3e7cfdff4f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:23:18 +0100 Subject: [PATCH 0527/1008] feat: ALC (#1514) * handle AWS_LAMBDA_LOG configuration * ALC documentation + code review * update doc --- docs/core/logging.md | 44 ++++- powertools-logging/pom.xml | 10 ++ .../logging/internal/LambdaLoggingAspect.java | 19 +- .../internal/LambdaTimestampResolver.java | 169 ++++++++++++++++++ .../LambdaTimestampResolverFactory.java | 49 +++++ .../logging/internal/LoggingConstants.java | 27 +++ .../src/main/resources/LambdaJsonLayout.json | 2 +- .../core/layout/LambdaJsonLayoutTest.java | 31 +++- 8 files changed, 344 insertions(+), 7 deletions(-) create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java diff --git a/docs/core/logging.md b/docs/core/logging.md index fd74db3d2..0391ae7b4 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -263,7 +263,7 @@ When debugging in non-production environments, you can instruct Logger to log th } ``` -### Customising fields in logs +### Customising fields in logs - 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: @@ -596,6 +596,48 @@ via `samplingRate` attribute on annotation. POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 ``` +## AWS Lambda Advanced Logging Controls +With AWS [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced), you can control the output format of your logs as either `TEXT` or `JSON` and specify the minimum accepted log level for your application. +Regardless of the output format setting in Lambda, Powertools for AWS Lambda will always output JSON formatted logging messages. + +When you have this feature enabled, log messages that don’t meet the configured log level are discarded by Lambda. +For example, if you set the minimum log level to `WARN`, you will only receive `WARN` and `ERROR` messages in your AWS CloudWatch Logs, all other log levels will be discarded by Lambda. + +```mermaid +sequenceDiagram + participant Lambda service + participant Lambda function + participant Application Logger + + Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN" + Lambda service->>Lambda function: Invoke (event) + Lambda function->>Lambda function: Calls handler + Lambda function->>Application Logger: logger.warn("Something happened") + Lambda function-->>Application Logger: logger.debug("Something happened") + Lambda function-->>Application Logger: logger.info("Something happened") + + Lambda service->>Lambda service: DROP INFO and DEBUG logs + + Lambda service->>CloudWatch Logs: Ingest error logs +``` + +Logger will automatically listen for the `AWS_LAMBDA_LOG_FORMAT` and `AWS_LAMBDA_LOG_LEVEL` environment variables, and change behaviour if they’re found to ensure as much compatibility as possible. + +### Priority of log level settings in Powertools for AWS Lambda + +When the Advanced Logging Controls feature is 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) for more details. + +We prioritise log level settings in this order: + +1. `AWS_LAMBDA_LOG_LEVEL` environment variable +2. `POWERTOOLS_LOG_LEVEL` environment variable + +In the event you have set `POWERTOOLS_LOG_LEVEL` to a level lower than the ACL setting, Powertools for AWS Lambda will output a warning log message informing you that your messages will be discarded by Lambda. + +### Timestamp format + +When the Advanced Logging Controls feature is enabled, Powertools for AWS Lambda must comply with the timestamp format required by AWS Lambda, which is [RFC3339](https://www.rfc-editor.org/rfc/rfc3339). +In this case the format will be `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`. ## Upgrade to JsonTemplateLayout from deprecated LambdaJsonLayout configuration in log4j2.xml diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index c31448e30..e4767893b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -137,6 +137,16 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <environmentVariables> + <AWS_LAMBDA_LOG_FORMAT>JSON</AWS_LAMBDA_LOG_FORMAT> + </environmentVariables> + </configuration> + </plugin> </plugins> </build> </project> \ No newline at end of file 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 ff556d89a..da770ccdb 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 @@ -28,6 +28,7 @@ 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.logging.internal.LoggingConstants.LAMBDA_LOG_LEVEL; import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.core.JsonPointer; @@ -63,17 +64,27 @@ @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 POWERTOOLS_LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); - private static final String LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); + private static final Random SAMPLER = new Random(); private static final String SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE"); private static Boolean LOG_EVENT; private static Level LEVEL_AT_INITIALISATION; static { - if (null != LOG_LEVEL) { - resetLogLevels(Level.getLevel(LOG_LEVEL)); + if (POWERTOOLS_LOG_LEVEL != null) { + Level powertoolsLevel = Level.getLevel(POWERTOOLS_LOG_LEVEL); + if (LAMBDA_LOG_LEVEL != null) { + Level lambdaLevel = Level.getLevel(LAMBDA_LOG_LEVEL); + if (powertoolsLevel.intLevel() > lambdaLevel.intLevel()) { + 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); + } + } + resetLogLevels(powertoolsLevel); + } else if (LAMBDA_LOG_LEVEL != null) { + resetLogLevels(Level.getLevel(LAMBDA_LOG_LEVEL)); } LEVEL_AT_INITIALISATION = LOG.getLevel(); diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java new file mode 100644 index 000000000..500b36c95 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java @@ -0,0 +1,169 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_FORMAT; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LOG_DATE_RFC3339_FORMAT; + +import java.util.Locale; +import java.util.TimeZone; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.time.MutableInstant; +import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults; +import org.apache.logging.log4j.layout.template.json.resolver.EventResolver; +import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig; +import org.apache.logging.log4j.layout.template.json.util.InstantFormatter; +import org.apache.logging.log4j.layout.template.json.util.JsonWriter; + +/** + * Default timestamp used by log4j is not RFC3339, which is used by Lambda internally to filter logs. + * When `AWS_LAMBDA_LOG_FORMAT` is set to JSON (i.e. using Lambda logging configuration), we should use the appropriate pattern, + * otherwise logs with invalid date format are considered as INFO. + * Inspired from org.apache.logging.log4j.layout.template.json.resolver.TimestampResolver + * + * TODO: remove in v2 an replace with the good pattern in LambdaJsonLayout.json + */ +public class LambdaTimestampResolver implements EventResolver { + + private final EventResolver internalResolver; + + public LambdaTimestampResolver(final TemplateResolverConfig config) { + final PatternResolverContext patternResolverContext = + PatternResolverContext.fromConfig(config); + internalResolver = new PatternResolver(patternResolverContext); + } + + @Override + public void resolve(LogEvent value, JsonWriter jsonWriter) { + internalResolver.resolve(value, jsonWriter); + } + + static String getName() { + return "lambda-timestamp"; + } + + private static final class PatternResolverContext { + + public static final String PATTERN = "pattern"; + private final InstantFormatter formatter; + + private final StringBuilder lastFormattedInstantBuffer = new StringBuilder(); + + private final MutableInstant lastFormattedInstant = new MutableInstant(); + + private PatternResolverContext( + final String pattern, + final TimeZone timeZone, + final Locale locale) { + this.formatter = InstantFormatter + .newBuilder() + .setPattern(pattern) + .setTimeZone(timeZone) + .setLocale(locale) + .build(); + lastFormattedInstant.initFromEpochSecond(-1, 0); + } + + private static PatternResolverContext fromConfig( + final TemplateResolverConfig config) { + final String pattern = readPattern(config); + final TimeZone timeZone = readTimeZone(config); + final Locale locale = config.getLocale(new String[]{PATTERN, "locale"}); + return new PatternResolverContext(pattern, timeZone, locale); + } + + private static String readPattern(final TemplateResolverConfig config) { + final String format = config.getString(new String[]{PATTERN, "format"}); + return format != null + ? format + : getLambdaTimestampFormatOrDefault(); + } + + private static String getLambdaTimestampFormatOrDefault() { + return "JSON".equals(LAMBDA_LOG_FORMAT) ? LOG_DATE_RFC3339_FORMAT : + JsonTemplateLayoutDefaults.getTimestampFormatPattern(); + } + + private static TimeZone readTimeZone(final TemplateResolverConfig config) { + final String timeZoneId = config.getString(new String[]{PATTERN, "timeZone"}); + if (timeZoneId == null) { + return JsonTemplateLayoutDefaults.getTimeZone(); + } + boolean found = false; + for (final String availableTimeZone : TimeZone.getAvailableIDs()) { + if (availableTimeZone.equalsIgnoreCase(timeZoneId)) { + found = true; + break; + } + } + if (!found) { + throw new IllegalArgumentException( + "invalid timestamp time zone: " + config); + } + return TimeZone.getTimeZone(timeZoneId); + } + + } + + private static final class PatternResolver implements EventResolver { + + private final PatternResolverContext patternResolverContext; + + private PatternResolver(final PatternResolverContext patternResolverContext) { + this.patternResolverContext = patternResolverContext; + } + + @Override + public synchronized void resolve( + final LogEvent logEvent, + final JsonWriter jsonWriter) { + + // Format timestamp if it doesn't match the last cached one. + final boolean instantMatching = patternResolverContext.formatter.isInstantMatching( + patternResolverContext.lastFormattedInstant, + logEvent.getInstant()); + if (!instantMatching) { + + // Format the timestamp. + patternResolverContext.lastFormattedInstantBuffer.setLength(0); + patternResolverContext.lastFormattedInstant.initFrom(logEvent.getInstant()); + patternResolverContext.formatter.format( + patternResolverContext.lastFormattedInstant, + patternResolverContext.lastFormattedInstantBuffer); + + // Write the formatted timestamp. + final StringBuilder jsonWriterStringBuilder = jsonWriter.getStringBuilder(); + final int startIndex = jsonWriterStringBuilder.length(); + jsonWriter.writeString(patternResolverContext.lastFormattedInstantBuffer); + + // Cache the written value. + patternResolverContext.lastFormattedInstantBuffer.setLength(0); + patternResolverContext.lastFormattedInstantBuffer.append( + jsonWriterStringBuilder, + startIndex, + jsonWriterStringBuilder.length()); + + } + + // Write the cached formatted timestamp. + else { + jsonWriter.writeRawString( + patternResolverContext.lastFormattedInstantBuffer); + } + + } + + } +} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java new file mode 100644 index 000000000..2022c6d4a --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.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.internal; + +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; + +@Plugin(name = "LambdaTimestampResolverFactory", category = TemplateResolverFactory.CATEGORY) +public final class LambdaTimestampResolverFactory implements EventResolverFactory { + + private static final LambdaTimestampResolverFactory INSTANCE = new LambdaTimestampResolverFactory(); + + private LambdaTimestampResolverFactory() { + } + + @PluginFactory + public static LambdaTimestampResolverFactory getInstance() { + return INSTANCE; + } + + @Override + public String getName() { + return LambdaTimestampResolver.getName(); + } + + @Override + public TemplateResolver<LogEvent> create(EventResolverContext context, + TemplateResolverConfig config) { + return new LambdaTimestampResolver(config); + } +} 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..e58ca4109 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.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.logging.internal; + +public class LoggingConstants { + public static final String LAMBDA_LOG_LEVEL = System.getenv("AWS_LAMBDA_LOG_LEVEL"); + + public static final String LAMBDA_LOG_FORMAT = System.getenv("AWS_LAMBDA_LOG_FORMAT"); + + public static final String LOG_DATE_RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + + private LoggingConstants() { + // constants + } +} diff --git a/powertools-logging/src/main/resources/LambdaJsonLayout.json b/powertools-logging/src/main/resources/LambdaJsonLayout.json index dfc1fc78f..da3385032 100644 --- a/powertools-logging/src/main/resources/LambdaJsonLayout.json +++ b/powertools-logging/src/main/resources/LambdaJsonLayout.json @@ -1,6 +1,6 @@ { "timestamp": { - "$resolver": "timestamp" + "$resolver": "lambda-timestamp" }, "instant": { "epochSecond": { 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 index 9b0c6165a..95fb9c47f 100644 --- 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 @@ -16,6 +16,7 @@ import static java.util.Collections.emptyMap; import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.when; @@ -32,8 +33,12 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; import java.util.Map; import org.apache.logging.log4j.Level; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -42,7 +47,6 @@ import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; class LambdaJsonLayoutTest { - private RequestHandler<Object, Object> handler = new PowerLogToolEnabled(); @Mock @@ -73,6 +77,31 @@ void shouldLogInStructuredFormat() throws IOException { .containsKey("service")); } + @Test + void shouldLogWithRFC3339TimestampFormat_WhenLambdaLoggingIsJSON() throws Exception { + // Given: AWS_LAMBDA_LOG_FORMAT=JSON defined in pom.xml + + // When + handler.handleRequest("test", context); + + // Then + assertThat(Files.lines(Paths.get("target/logfile.json"))) + .hasSize(1) + .allSatisfy(line -> assertThat(parseToMap(line)) + .extracting("timestamp", as(InstanceOfAssertFactories.STRING)) + .satisfies(s -> assertThat(hasDateFormat(s, "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")).isTrue())); + } + + private boolean hasDateFormat(String timestamp, String format) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT); + try { + dtf.parse(timestamp); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + @Test void shouldModifyLogLevelBasedOnEnvVariable() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { From 877ab51c10e19db78a06ee703121ac749342d043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:36:51 +0100 Subject: [PATCH 0528/1008] chore:Prep release 1.18.0 (#1515) * chore:prep release 1.18.0 * update version * update version in kotlin example * maven local repo in gradle example * update changelog --------- Co-authored-by: scottgerring <scottgerring@users.noreply.github.com> --- CHANGELOG.md | 31 +++++++++++++++++++ README.md | 8 ++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- .../pom.xml | 2 +- .../powertools-examples-core/cdk/app/pom.xml | 2 +- .../cdk/infra/pom.xml | 2 +- .../gradle/build.gradle | 7 +++-- .../kotlin/build.gradle.kts | 6 ++-- examples/powertools-examples-core/sam/pom.xml | 2 +- .../serverless/pom.xml | 2 +- .../terraform/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- .../powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- .../powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 34 files changed, 72 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb90ce3f..4b9f664fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,37 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.18.0] - 2023-11-16 + +### Added + +* feat: add support for [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced) (#1514) by @jeromevdl +* feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) by @AlexeySoshin + +### Maintenance + +* fix: json schema 403 error (#1457) by @jeromevdl +* fix: array jmespath fail in idempotency module (#1420) by @jeromevdl +* chore: java21 support in our build (#1488) by @jeromevdl +* chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) by @jdoherty +* fix: null namespace should fallback to default namespace (#1506) by @jeromevdl +* fix: get trace id from system property when env var is not set (#1503) by @mriccia +* chore: artifacts size on good branches (#1493) by @jeromevdl +* fix: enforce jackson databind version (#1472) by @jeromevdl +* chore: add missing projects and improve workflow (#1487) by @jeromevdl +* chore: Reporting size of the jars in GitHub comments (#1196) by @jeromevdl +* Deps: Bump third party dependencies to the latest versions. + +### Documentation + +* docs(customer-reference): add Vertex Pharmaceuticals as a customer reference (#1486) by @scottgerring +* docs: Adding Kotlin example. (#1454) by @jasoniharris +* docs: Terraform example (#1478) by @skal111 +* docs: Add Serveless Framework example (#1363) by @AlexeySoshin +* docs: Fix link to SQS large message migration guide (#1422) by @scottgerring +* docs(logging): correct log example keys (#1411) by @walmsles +* docs: Update gradle configuration readme (#1359) by @scottgerring + ## [1.17.0] - 2023-08-21 ### Added diff --git a/README.md b/README.md index 900395554..63ba35cc6 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.17.0</version> + <version>1.18.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.17.0</version> + <version>1.18.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.17.0</version> + <version>1.18.0</version> </dependency> ... </dependencies> @@ -190,7 +190,7 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ## Examples -See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/v1.17.0/examples)** for example projects showcasing usage of different utilities. +See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/v1.18.0/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). diff --git a/examples/pom.xml b/examples/pom.xml index 900f095f8..428c74af8 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 99d3a97cc..af3655b9a 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c195cd262..2df1ce9f6 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index b4652bd65..9d9435ac3 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.16.1</version> + <version>1.17.0</version> <artifactId>powertools-examples-core-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 216bf35d4..fd3cd313b 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.100.0</cdk.version> diff --git a/examples/powertools-examples-core/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle index 06aec59e8..0d6b0eee8 100644 --- a/examples/powertools-examples-core/gradle/build.gradle +++ b/examples/powertools-examples-core/gradle/build.gradle @@ -18,6 +18,7 @@ compileJava { } repositories { + mavenLocal() mavenCentral() } @@ -27,8 +28,8 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' - aspect 'software.amazon.lambda:powertools-tracing:1.17.0' - aspect 'software.amazon.lambda:powertools-logging:1.17.0' - aspect 'software.amazon.lambda:powertools-metrics:1.17.0' + aspect 'software.amazon.lambda:powertools-tracing:1.18.0' + aspect 'software.amazon.lambda:powertools-logging:1.18.0' + aspect 'software.amazon.lambda:powertools-metrics:1.18.0' } diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index fc363c1b9..f95d1099d 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -14,9 +14,9 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:2.13.2.2") implementation("com.amazonaws:aws-lambda-java-events:3.11.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2") - aspect("software.amazon.lambda:powertools-tracing:1.18.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-logging:1.18.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-metrics:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-tracing:1.18.0") + aspect("software.amazon.lambda:powertools-logging:1.18.0") + aspect("software.amazon.lambda:powertools-metrics:1.18.0") testImplementation("junit:junit:4.13.2") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 997472344..adb5e13eb 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-core-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 6fce1d2dd..36c7e7280 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-core-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 2a55c31bc..532675717 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-core-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 4b8eba9c9..5e79debf4 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 0e07b0cbb..495d6c59c 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 495fe8b77..e54c84315 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0338fe7d9..9d0b3f2a8 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 65628f6b2..3cb4264c0 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.18.0-SNAPSHOT</version> + <version>1.18.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 0f9c065a7..a271c1260 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,7 +88,7 @@ extra_javascript: extra: powertools: - version: 1.17.0 # to update after each release (we do not want snapshot version here) + version: 1.18.0 # to update after each release (we do not want snapshot version here) repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index e1a0c5a6c..76b59d747 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 0c59ce497..cedbcf317 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index f6cd822a6..660f6e34a 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index b848e9f79..0d82530a0 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 6507e3104..995aeb1c7 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.18.0-SNAPSHOT</lambda.powertools.version> + <lambda.powertools.version>1.18.0</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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 84760eac8..d2aeaed66 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 8b3459dee..fe85d74fd 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 8fdae2139..2d5540c54 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index e4767893b..6fd4ce89f 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index eff7296c8..677e18ed7 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 808232ab1..0e86a0a06 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e5c4087fe..06b828923 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index c4b2b54f0..e14ec59b5 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 34e80b06e..a6e84c4ac 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d98a97e2a..f16bedd2f 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 24de0ba2a..a372e4740 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0-SNAPSHOT</version> + <version>1.18.0</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 2602ea7a058d6f644d36bb81e0eabd1ce15ae0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:44:08 +0100 Subject: [PATCH 0529/1008] chore: update version to next snapshot: 1-19.0-SNAPSHOT (#1516) * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * building only for LTS --- .github/workflows/pr_build.yml | 2 +- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- examples/powertools-examples-core/gradle/build.gradle | 6 +++--- examples/powertools-examples-core/kotlin/build.gradle.kts | 6 +++--- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-core/serverless/pom.xml | 2 +- examples/powertools-examples-core/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 31 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index c316c0073..3cf3a5425 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -53,7 +53,7 @@ jobs: strategy: max-parallel: 5 matrix: - java: [8, 11, 17, 21, 15, 16, 18, 19, 20] + java: [8, 11, 17, 21] name: Java ${{ matrix.java }} env: JAVA: ${{ matrix.java }} diff --git a/examples/pom.xml b/examples/pom.xml index 428c74af8..5c423f251 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index af3655b9a..c8f829997 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 2df1ce9f6..54d6b50ef 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index fd3cd313b..e2a1d6b9a 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.100.0</cdk.version> diff --git a/examples/powertools-examples-core/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle index 0d6b0eee8..520b689cb 100644 --- a/examples/powertools-examples-core/gradle/build.gradle +++ b/examples/powertools-examples-core/gradle/build.gradle @@ -28,8 +28,8 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' - aspect 'software.amazon.lambda:powertools-tracing:1.18.0' - aspect 'software.amazon.lambda:powertools-logging:1.18.0' - aspect 'software.amazon.lambda:powertools-metrics:1.18.0' + aspect 'software.amazon.lambda:powertools-tracing:1.19.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-logging:1.19.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-metrics:1.19.0-SNAPSHOT' } diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index f95d1099d..4a167f225 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -14,9 +14,9 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:2.13.2.2") implementation("com.amazonaws:aws-lambda-java-events:3.11.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2") - aspect("software.amazon.lambda:powertools-tracing:1.18.0") - aspect("software.amazon.lambda:powertools-logging:1.18.0") - aspect("software.amazon.lambda:powertools-metrics:1.18.0") + aspect("software.amazon.lambda:powertools-tracing:1.19.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-logging:1.19.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-metrics:1.19.0-SNAPSHOT") testImplementation("junit:junit:4.13.2") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index adb5e13eb..16f82ebd6 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-core-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 36c7e7280..f8d9ab684 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-core-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 532675717..eef73207e 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-core-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 5e79debf4..bf4c041bf 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 495d6c59c..2aa813ff4 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ 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.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index e54c84315..4f2b8ffda 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 9d0b3f2a8..a6737bfed 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ 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.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-sqs</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 3cb4264c0..42d484ed1 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.18.0</version> + <version>1.19.0-SNAPSHOT</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) library Examples - Validation</name> diff --git a/pom.xml b/pom.xml index 76b59d747..0ac62cbda 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index cedbcf317..fabaeba30 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 660f6e34a..54c104468 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java)library Cloudformation</name> diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 0d82530a0..041950cf5 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Core</name> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 995aeb1c7..502b14eb3 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> <properties> - <lambda.powertools.version>1.18.0</lambda.powertools.version> + <lambda.powertools.version>1.19.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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index d2aeaed66..05c544641 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index fe85d74fd..94aceebfc 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 2d5540c54..04e46dc6e 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 6fd4ce89f..78c36f41a 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Logging</name> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 677e18ed7..53df4cf57 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 0e86a0a06..788ac9438 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 06b828923..70280a0b8 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index e14ec59b5..d65458d5e 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library SQS</name> diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index a6e84c4ac..50e473ad1 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Test Suite</name> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index f16bedd2f..26bc0aa16 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) library Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index a372e4740..11ad1398f 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>1.18.0</version> + <version>1.19.0-SNAPSHOT</version> </parent> <name>Powertools for AWS Lambda (Java) validation library</name> From 0c9b9ab9a542e581a06faf41deac0f1acc053378 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Fri, 17 Nov 2023 13:46:53 +0100 Subject: [PATCH 0530/1008] Add some more margin to the test pause (#1518) --- .../amazon/lambda/powertools/LargeMessageIdempotentE2ET.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..ef342ea13 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 @@ -107,7 +107,7 @@ public static void tearDown() { @Test public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throws InterruptedException, IOException { - int waitMs = 10000; + int waitMs = 15000; // GIVEN InputStream inputStream = this.getClass().getResourceAsStream("/large_sqs_message.txt"); From 76cd36306633489b1e1651e1fb281b267922f2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:00:23 +0100 Subject: [PATCH 0531/1008] test: e2e tests with java 21 (#1517) * e2e tests with java 21 * Run Java21 tests using the Java17 compiler * Run all of the E2E tests in parallel, not just the first 3 * Try again * . * Let's try again * Add some comment on Java21 to the repo * Add caveat about lambda runtimes * Clean up wording a little --------- Co-authored-by: Scott Gerring <gerrings@amazon.com> --- .github/workflows/run-e2e-tests.yml | 15 ++++++--- README.md | 8 +++++ docs/index.md | 10 +++++- powertools-e2e-tests/pom.xml | 2 +- .../powertools/testutils/Infrastructure.java | 32 ++++++++++++------- .../powertools/testutils/JavaRuntime.java | 3 +- 6 files changed, 51 insertions(+), 19 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index a2a1b9aec..9e71f56af 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -32,13 +32,19 @@ jobs: e2e: runs-on: ubuntu-latest strategy: - max-parallel: 3 + max-parallel: 4 matrix: - java: [ 8, 11, 17 ] + java: [ 8, 11, 17, 21 ] name: End-to-end tests java${{ matrix.java }} env: - JAVA_VERSION: ${{ matrix.java }} AWS_DEFAULT_REGION: eu-west-1 + + # If matrix.version is 21, use 17, otherwise use matrix.version + # This is because AspectJ does not yet support weaving with Java21; we want + # to test the Java21 runtime, but we can't yet use the JDK21 compiler. + # https://github.com/eclipse-aspectj/aspectj/issues/260#issuecomment-1815920274 + JAVA_VERSION: ${{ (matrix.java == 21 && '17') || matrix.java }} + JAVA_LAMBDA_RUNTIME_VERSION: ${{ matrix.java }} permissions: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read @@ -48,7 +54,8 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - java-version: ${{ matrix.java }} + # See comment above on JAVA_VERSION env var + java-version: ${{ (matrix.java == 21 && '17') || matrix.java }} cache: maven - name: Setup AWS credentials uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 diff --git a/README.md b/README.md index 63ba35cc6..bdf779bfb 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,14 @@ Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless **[📜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/)** +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the +[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + +AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). +If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 +Lambda runtimes. + ### Installation Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it diff --git a/docs/index.md b/docs/index.md index 92589be7c..b1b55e2d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,10 +11,18 @@ Powertools for AWS Lambda (Java) is a suite of utilities for AWS Lambda Function 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?" +???+ 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, 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. +???+ tip "Java Compatability" + Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the + [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + + AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). + If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 + Lambda runtimes. + ## Tenets This project separates core utilities that will be available in other runtimes vs general utilities that might not be available across all runtimes. diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 05c544641..8a0b65f32 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.3.0</constructs.version> - <cdk.version>2.100.0</cdk.version> + <cdk.version>2.109.0</cdk.version> </properties> <dependencies> 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..11ee24b05 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 @@ -114,6 +114,7 @@ public class Infrastructure { private final String queue; private final String kinesisStream; private final String largeMessagesBucket; + private final JavaRuntime lambdaRuntimeVersion; private String ddbStreamsTableName; private String functionName; private Object cfnTemplate; @@ -124,6 +125,7 @@ private Infrastructure(Builder builder) { this.tracing = builder.tracing; this.envVar = builder.environmentVariables; this.runtime = builder.runtime; + this.lambdaRuntimeVersion = builder.lambdaRuntimeVersion; this.timeout = builder.timeoutInSeconds; this.pathToFunction = builder.pathToFunction; this.idempotencyTable = builder.idemPotencyTable; @@ -204,6 +206,7 @@ public void destroy() { private Stack createStackWithLambda() { boolean createTableForAsyncTests = false; Stack stack = new Stack(app, stackName); + List<String> packagingInstruction = Arrays.asList( "/bin/sh", "-c", @@ -247,7 +250,7 @@ private Stack createStackWithLambda() { .handler("software.amazon.lambda.powertools.e2e.Function::handleRequest") .memorySize(1024) .timeout(Duration.seconds(timeout)) - .runtime(runtime.getCdkRuntime()) + .runtime(lambdaRuntimeVersion.getCdkRuntime()) .environment(envVar) .tracing(tracing ? Tracing.ACTIVE : Tracing.DISABLED) .build(); @@ -504,29 +507,34 @@ public static class Builder { private String queue; private String kinesisStream; private String ddbStreamsTableName; + private JavaRuntime lambdaRuntimeVersion; private Builder() { - getJavaRuntime(); + runtime = mapRuntimeVersion("JAVA_VERSION"); + lambdaRuntimeVersion = mapRuntimeVersion("JAVA_LAMBDA_RUNTIME_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("JAVA_LAMBDA_RUNTIME_VERSION is not set"); } if (javaVersion.startsWith("8")) { - runtime = JavaRuntime.JAVA8AL2; + ret = JavaRuntime.JAVA8AL2; } else if (javaVersion.startsWith("11")) { - runtime = JavaRuntime.JAVA11; + ret = JavaRuntime.JAVA11; } else if (javaVersion.startsWith("17")) { - runtime = JavaRuntime.JAVA17; + ret = JavaRuntime.JAVA17; + } else if (javaVersion.startsWith("21")) { + ret = JavaRuntime.JAVA21; } 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..c75682949 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 @@ -20,7 +20,8 @@ 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"); private final String runtime; private final Runtime cdkRuntime; From ff4ee6890d2bc3ee1328c7c182185dc473a5a44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 20 Nov 2023 07:46:09 +0100 Subject: [PATCH 0532/1008] update doc for ALC (#1520) --- docs/core/logging.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index 0391ae7b4..70781b1b2 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -596,12 +596,18 @@ via `samplingRate` attribute on annotation. POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 ``` -## AWS Lambda Advanced Logging Controls -With AWS [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced), you can control the output format of your logs as either `TEXT` or `JSON` and specify the minimum accepted log level for your application. -Regardless of the output format setting in Lambda, Powertools for AWS Lambda will always output JSON formatted logging messages. +## AWS Lambda Advanced Logging Controls (ALC) -When you have this feature enabled, log messages that don’t meet the configured log level are discarded by Lambda. -For example, if you set the minimum log level to `WARN`, you will only receive `WARN` and `ERROR` messages in your AWS CloudWatch Logs, all other log levels will be discarded by Lambda. +!!!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. + +<!-- 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. + +When enabled, you should keep `Logger` and ALC log level in sync to avoid data loss. + +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 @@ -610,29 +616,29 @@ sequenceDiagram participant Application Logger Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN" + Note over Application Logger: POWERTOOLS_LOG_LEVEL="DEBUG" + Lambda service->>Lambda function: Invoke (event) Lambda function->>Lambda function: Calls handler - Lambda function->>Application Logger: logger.warn("Something happened") + 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->>Lambda service: DROP INFO and DEBUG logs - + Lambda service--xLambda service: DROP INFO and DEBUG logs Lambda service->>CloudWatch Logs: Ingest error logs ``` -Logger will automatically listen for the `AWS_LAMBDA_LOG_FORMAT` and `AWS_LAMBDA_LOG_LEVEL` environment variables, and change behaviour if they’re found to ensure as much compatibility as possible. - ### Priority of log level settings in Powertools for AWS Lambda -When the Advanced Logging Controls feature is 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) for more details. - We prioritise log level settings in this order: 1. `AWS_LAMBDA_LOG_LEVEL` environment variable 2. `POWERTOOLS_LOG_LEVEL` environment variable -In the event you have set `POWERTOOLS_LOG_LEVEL` to a level lower than the ACL setting, Powertools for AWS Lambda will output a warning log message informing you that your messages will be discarded by Lambda. +If you set `Logger` 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. ### Timestamp format From 8e4173a086158a539d3ab040f0a0d274807c00eb Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:19:30 +0100 Subject: [PATCH 0533/1008] chore: Testing java21 aspectj pre-release (#1519) * e2e tests with java 21 * use aspectj 1.9.21-SNAPSHOT * Fix log4j2.xml missing in logging test for java21 * rollback double runtime * remove comment * keep aspectj 1.9.7 in parent for java8 compatibility * use M1 instead of snapshot * update documentation for aspectj * update documentation for aspectj --------- Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> --- README.md | 39 ++++-- docs/index.md | 43 ++++-- pom.xml | 3 +- powertools-e2e-tests/handlers/logging/pom.xml | 4 + powertools-e2e-tests/handlers/pom.xml | 126 +++++++----------- .../powertools/testutils/Infrastructure.java | 9 +- 6 files changed, 122 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index bdf779bfb..dbe8eb2fe 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,11 @@ Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless **[📜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/)** -### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the -[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). - -AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). -If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 -Lambda runtimes. - -### 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> ... @@ -196,6 +188,33 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ``` </details> +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as 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 good 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> + +| JDK version | aspectj version | +|-------------|-----------------| +| `1.8` | `1.9.7` | +| `11-17` | `1.9.20.1` | +| `21` | `1.9.21` | + +More info [here](https://github.com/aws-powertools/powertools-lambda-java/pull/1519/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R191). + +</details> + ## Examples See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/v1.18.0/examples)** for example projects showcasing usage of different utilities. diff --git a/docs/index.md b/docs/index.md index b1b55e2d6..884e02476 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,14 +15,6 @@ Powertools for AWS Lambda (Java) is a suite of utilities for AWS Lambda Function 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, 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. -???+ tip "Java Compatability" - Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the - [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). - - AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). - If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 - Lambda runtimes. - ## Tenets This project separates core utilities that will be available in other runtimes vs general utilities that might not be available across all runtimes. @@ -282,6 +274,41 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl 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+. +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the +[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + +For the following modules, Powertools for AWS Lambda (Java) leverages the **aspectj** library to provide annotations: +- Logging +- Metrics +- Tracing +- Parameters +- Idempotency +- Validation +- Large messages + + +You may need to add the good 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/dist/doc/JavaVersionCompatibility.md) between this library and the JDK: + +| JDK version | aspectj version | +|-------------|-----------------| +| `1.8` | `1.9.7` | +| `11-17` | `1.9.20.1` | +| `21` | `1.9.21` | + +_Note: 1.9.21 is not yet available and Java 21 not yet officially supported by aspectj, but you can already use the `1.9.21.M1`_ + + ## Environment variables !!! info diff --git a/pom.xml b/pom.xml index 0ac62cbda..3c30dc260 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,6 @@ <maven.compiler.target>1.8</maven.compiler.target> <log4j.version>2.20.0</log4j.version> <jackson.version>2.15.3</jackson.version> - <aspectj.version>1.9.7</aspectj.version> <aws.sdk.version>2.21.0</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> @@ -84,6 +83,7 @@ <lambda.events.version>3.11.3</lambda.events.version> <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</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> @@ -516,6 +516,7 @@ </plugins> </build> </profile> + <profile> <id>jdk16</id> <activation> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 4b613f2bf..61ec6b414 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -17,6 +17,10 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 502b14eb3..a9096477b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -42,7 +42,11 @@ <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</artifactId> @@ -143,96 +147,66 @@ <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> </plugins> </pluginManagement> </build> <profiles> + <!-- https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md --> <profile> - <id>jdk8</id> + <id>jdk8to16</id> <activation> - <jdk>(,11)</jdk> <!-- < 11 --> + <jdk>[1.8,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.M1</aspectj.version> + </properties> </profile> </profiles> 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 11ee24b05..28a0f2bb4 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 @@ -114,7 +114,6 @@ public class Infrastructure { private final String queue; private final String kinesisStream; private final String largeMessagesBucket; - private final JavaRuntime lambdaRuntimeVersion; private String ddbStreamsTableName; private String functionName; private Object cfnTemplate; @@ -125,7 +124,6 @@ private Infrastructure(Builder builder) { this.tracing = builder.tracing; this.envVar = builder.environmentVariables; this.runtime = builder.runtime; - this.lambdaRuntimeVersion = builder.lambdaRuntimeVersion; this.timeout = builder.timeoutInSeconds; this.pathToFunction = builder.pathToFunction; this.idempotencyTable = builder.idemPotencyTable; @@ -213,7 +211,6 @@ private Stack createStackWithLambda() { "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/" @@ -250,7 +247,7 @@ private Stack createStackWithLambda() { .handler("software.amazon.lambda.powertools.e2e.Function::handleRequest") .memorySize(1024) .timeout(Duration.seconds(timeout)) - .runtime(lambdaRuntimeVersion.getCdkRuntime()) + .runtime(runtime.getCdkRuntime()) .environment(envVar) .tracing(tracing ? Tracing.ACTIVE : Tracing.DISABLED) .build(); @@ -507,11 +504,9 @@ public static class Builder { private String queue; private String kinesisStream; private String ddbStreamsTableName; - private JavaRuntime lambdaRuntimeVersion; private Builder() { runtime = mapRuntimeVersion("JAVA_VERSION"); - lambdaRuntimeVersion = mapRuntimeVersion("JAVA_LAMBDA_RUNTIME_VERSION"); } @@ -520,7 +515,7 @@ 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_LAMBDA_RUNTIME_VERSION is not set"); + throw new IllegalArgumentException(environmentVariableName + " is not set"); } if (javaVersion.startsWith("8")) { ret = JavaRuntime.JAVA8AL2; From 26730af1a93b962220efe8cf3a4b426e63095804 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Tue, 21 Nov 2023 16:47:06 +0100 Subject: [PATCH 0534/1008] Fix POM --- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 520b689cb..e85936350 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -28,8 +28,8 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' implementation 'com.amazonaws:aws-lambda-java-events:3.11.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' - aspect 'software.amazon.lambda:powertools-tracing:1.19.0-SNAPSHOT' - aspect 'software.amazon.lambda:powertools-logging:1.19.0-SNAPSHOT' - aspect 'software.amazon.lambda:powertools-metrics:1.19.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-tracing:2.0.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-logging:2.0.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT' } From 628a2ca93223df2b2962d1bc122f32a6f572b382 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 22 Nov 2023 09:07:00 +0100 Subject: [PATCH 0535/1008] chore: Remove build cruft --- .github/workflows/run-e2e-tests.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 9e71f56af..626268214 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -38,13 +38,7 @@ jobs: name: End-to-end tests java${{ matrix.java }} env: AWS_DEFAULT_REGION: eu-west-1 - - # If matrix.version is 21, use 17, otherwise use matrix.version - # This is because AspectJ does not yet support weaving with Java21; we want - # to test the Java21 runtime, but we can't yet use the JDK21 compiler. - # https://github.com/eclipse-aspectj/aspectj/issues/260#issuecomment-1815920274 - JAVA_VERSION: ${{ (matrix.java == 21 && '17') || matrix.java }} - JAVA_LAMBDA_RUNTIME_VERSION: ${{ matrix.java }} + JAVA_VERSION: ${{ matrix.java }} permissions: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read @@ -54,8 +48,7 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - # See comment above on JAVA_VERSION env var - java-version: ${{ (matrix.java == 21 && '17') || matrix.java }} + java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 From 6141243f6c530c73669d318032fe07b28c36d26b Mon Sep 17 00:00:00 2001 From: jdoherty <jdoherty07@gmail.com> Date: Fri, 1 Dec 2023 09:31:57 +0000 Subject: [PATCH 0536/1008] chore: remove Java 8 from v2 examples (#1531) * Remove Java 8 issue #1466 * reverting pom to java 8 as change should only apply to examples * updated workflow to not build examples in java 8 workflow * updated syntax of workflow * excluding examples from spotbugs check for java 8 build * updated github actions workflow * updated workflows to have a comma seperated list of examples modules when java 8 to exclude * updating build syntax for non java 8 * updating gradle config for Java 11+ * updating the java version on spotbugs to 11 --- .github/workflows/pr_build.yml | 8 ++++-- .github/workflows/spotbugs.yml | 2 +- examples/README.md | 2 -- examples/powertools-examples-batch/pom.xml | 25 ++----------------- .../README.md | 11 +++++++- .../pom.xml | 24 ++---------------- .../cdk/app/pom.xml | 24 ++---------------- .../cdk/infra/pom.xml | 4 +-- .../gradle/README.md | 2 +- .../gradle/build.gradle | 8 +++--- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/template.yaml | 4 +-- .../kotlin/README.md | 2 +- .../kotlin/build.gradle.kts | 21 +++------------- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../sam/pom.xml | 24 ++---------------- .../serverless/pom.xml | 25 +++---------------- .../terraform/pom.xml | 24 ++---------------- .../powertools-examples-idempotency/pom.xml | 24 ++---------------- .../powertools-examples-parameters/pom.xml | 24 ++---------------- .../powertools-examples-serialization/pom.xml | 12 ++------- .../powertools-examples-validation/README.md | 2 +- 22 files changed, 53 insertions(+), 223 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index eb3cc053d..e86f49753 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -66,13 +66,17 @@ jobs: java-version: ${{ matrix.java }} cache: 'maven' - name: Build with Maven + if: ${{ matrix.java == '8' }} # If 8 exclude the examples directory + run: mvn -B install --file pom.xml -pl '!software.amazon.lambda.examples:powertools-examples-idempotency,!software.amazon.lambda.examples:powertools-examples-batch,!software.amazon.lambda.examples:powertools-examples-cloudformation,!software.amazon.lambda.examples:powertools-examples-core-utilities-cdk,!software.amazon.lambda.examples:powertools-examples-core-utilities-sam,!software.amazon.lambda.examples:powertools-examples-core-utilities-serverless,!software.amazon.lambda.examples:powertools-examples-core-utilities-terraform,!software.amazon.lambda.examples:powertools-examples-parameters,!software.amazon.lambda.examples:powertools-examples-serialization,!software.amazon.lambda.examples:powertools-examples-validation,!software.amazon.lambda.examples:cdk,!software.amazon.lambda:powertools-examples' + - name: Build with Maven + if: ${{ matrix.java != '8' }} # If not 8 don't exclude the examples directory run: mvn -B install --file pom.xml - name: Build Gradle Example - Java - if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 + if: ${{ matrix.java != '8' }} working-directory: examples/powertools-examples-core-utilities/gradle run: ./gradlew build - name: Build Gradle Example - Kotlin - if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 + if: ${{ matrix.java != '8' }} working-directory: examples/powertools-examples-core-utilities/kotlin run: ./gradlew build - name: Setup Terraform diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index d314107fa..f0d50d8c5 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - java-version: 8 + java-version: 11 # 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. diff --git a/examples/README.md b/examples/README.md index aa8457def..2e34513db 100644 --- a/examples/README.md +++ b/examples/README.md @@ -63,8 +63,6 @@ If you're not using SAM, you can look for examples for other tools under [powert 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) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index a5115aeeb..36660bc96 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -12,8 +12,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> <sdk.version>2.21.1</sdk.version> </properties> @@ -143,25 +143,4 @@ </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> - </profile> - </profiles> </project> \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 4b53ff0aa..f0a15f1cd 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-20230718 +``` ### 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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d179fbe2e..864ea5fe6 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -11,8 +11,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> <aws.sdk.version>2.21.0</aws.sdk.version> @@ -169,24 +169,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index be8c9ca67..822e87633 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -12,8 +12,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> </properties> @@ -138,24 +138,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index d2b05b122..053bd239c 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -18,8 +18,8 @@ <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> - <source>1.8</source> - <target>1.8</target> + <source>11</source> + <target>11</target> </configuration> </plugin> <plugin> diff --git a/examples/powertools-examples-core-utilities/gradle/README.md b/examples/powertools-examples-core-utilities/gradle/README.md index c7c798d5d..adc7fe635 100644 --- a/examples/powertools-examples-core-utilities/gradle/README.md +++ b/examples/powertools-examples-core-utilities/gradle/README.md @@ -1,7 +1,7 @@ # 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 1.8 only; in order to use a newer version, check out the Gradle +[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**, diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index e85936350..03971c406 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -1,16 +1,16 @@ plugins { id 'java' - id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" + id "io.freefair.aspectj.post-compile-weaving" version "8.2.2" } wrapper { - gradleVersion = "7.6.1" + gradleVersion = "8.5" } compileJava { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" + sourceCompatibility = "11" + targetCompatibility = "11" ajc { enabled = true 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 index 0d23ac00c..1af9e0930 100644 --- a/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/examples/powertools-examples-core-utilities/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/powertools-examples-core-utilities/gradle/template.yaml b/examples/powertools-examples-core-utilities/gradle/template.yaml index a717c2998..1a1572fca 100644 --- a/examples/powertools-examples-core-utilities/gradle/template.yaml +++ b/examples/powertools-examples-core-utilities/gradle/template.yaml @@ -26,7 +26,7 @@ Resources: Properties: CodeUri: . Handler: helloworld.App::handleRequest - Runtime: java8 + 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: @@ -43,7 +43,7 @@ Resources: Properties: CodeUri: . Handler: helloworld.AppStream::handleRequest - Runtime: java8 + Runtime: java11 MemorySize: 512 Tracing: Active Environment: diff --git a/examples/powertools-examples-core-utilities/kotlin/README.md b/examples/powertools-examples-core-utilities/kotlin/README.md index da9ec5b52..422e12742 100644 --- a/examples/powertools-examples-core-utilities/kotlin/README.md +++ b/examples/powertools-examples-core-utilities/kotlin/README.md @@ -1,7 +1,7 @@ # 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 1.8 only; in order to use a newer version, check out the Gradle +[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**, diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 29a29b0c4..1c16db0db 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("io.freefair.aspectj.post-compile-weaving") version "6.6.3" + id("io.freefair.aspectj.post-compile-weaving") version "8.2.2" kotlin("jvm") version "1.9.10" } @@ -21,19 +21,6 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } -tasks.compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} - -tasks.compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} - -// If using JDK 11 or higher, use the following instead: -//kotlin { -// jvmToolchain(11) -//} \ No newline at end of file +kotlin { + jvmToolchain(11) +} \ No newline at end of file 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 index 0d23ac00c..1af9e0930 100644 --- a/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/examples/powertools-examples-core-utilities/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index a88150f4b..3ddecc5e4 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -10,8 +10,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> </properties> @@ -126,24 +126,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index c92d0b653..7ebfdfc00 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -10,8 +10,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> </properties> @@ -59,6 +59,7 @@ </dependencies> <build> + <finalName>helloworld-lambda</finalName> <plugins> <plugin> <groupId>dev.aspectj</groupId> @@ -126,24 +127,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index d74ce7085..f508d9d3d 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -10,8 +10,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> </properties> @@ -145,24 +145,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 901074ddb..b7fd3d832 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -24,8 +24,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> </properties> @@ -198,24 +198,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index cffab7a65..7a4af628c 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -8,8 +8,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20</aspectj.version> </properties> @@ -108,24 +108,4 @@ </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> - </profile> - </profiles> </project> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 5d7036497..468c7d161 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -8,8 +8,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> @@ -37,14 +37,6 @@ <build> <plugins> - <!-- Don't deploy the example --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <configuration> - <skip>true</skip> - </configuration> - </plugin> <!-- Don't deploy the example --> <plugin> <groupId>org.apache.maven.plugins</groupId> 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, From 412118dc1def72f4c128e042cd3f23277c976e3a Mon Sep 17 00:00:00 2001 From: skal111 <p_romanens@hotmail.com> Date: Fri, 1 Dec 2023 16:59:52 +0000 Subject: [PATCH 0537/1008] feat(v2): Validation failures return 400s (#1489) --- docs/utilities/validation.md | 5 +- powertools-e2e-tests/handlers/pom.xml | 6 + .../handlers/validation-alb-event/pom.xml | 60 +++++++ .../lambda/powertools/e2e/Function.java | 36 ++++ .../src/main/resources/log4j2.xml | 16 ++ .../resources/validation/inbound_schema.json | 32 ++++ .../resources/validation/outbound_schema.json | 32 ++++ .../handlers/validation-apigw-event/pom.xml | 60 +++++++ .../lambda/powertools/e2e/Function.java | 33 ++++ .../src/main/resources/log4j2.xml | 16 ++ .../resources/validation/inbound_schema.json | 32 ++++ .../resources/validation/outbound_schema.json | 32 ++++ .../lambda/powertools/ValidationALBE2ET.java | 104 ++++++++++++ .../powertools/ValidationApiGWE2ET.java | 107 ++++++++++++ .../validation/invalid_alb_in_event.json | 28 +++ .../validation/invalid_alb_out_event.json | 28 +++ .../validation/invalid_api_gw_in_event.json | 62 +++++++ .../validation/invalid_api_gw_out_event.json | 62 +++++++ .../validation/valid_alb_in_out_event.json | 28 +++ .../validation/valid_api_gw_in_out_event.json | 62 +++++++ .../validation/internal/ValidationAspect.java | 131 ++++++++++++--- ...aV7APIGatewayProxyRequestEventHandler.java | 34 ++++ ...java => GenericSchemaV7StringHandler.java} | 5 +- ...nInboundAPIGatewayV2HTTPEventHandler.java} | 11 +- ...andledResponseEventsArgumentsProvider.java | 64 +++++++ .../ResponseEventsArgumentsProvider.java | 7 +- .../internal/ValidationAspectTest.java | 159 ++++++++++++++---- 27 files changed, 1176 insertions(+), 76 deletions(-) create mode 100644 powertools-e2e-tests/handlers/validation-alb-event/pom.xml create mode 100644 powertools-e2e-tests/handlers/validation-alb-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/inbound_schema.json create mode 100644 powertools-e2e-tests/handlers/validation-alb-event/src/main/resources/validation/outbound_schema.json create mode 100644 powertools-e2e-tests/handlers/validation-apigw-event/pom.xml create mode 100644 powertools-e2e-tests/handlers/validation-apigw-event/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/inbound_schema.json create mode 100644 powertools-e2e-tests/handlers/validation-apigw-event/src/main/resources/validation/outbound_schema.json create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationALBE2ET.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationApiGWE2ET.java create mode 100644 powertools-e2e-tests/src/test/resources/validation/invalid_alb_in_event.json create mode 100644 powertools-e2e-tests/src/test/resources/validation/invalid_alb_out_event.json create mode 100644 powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_in_event.json create mode 100644 powertools-e2e-tests/src/test/resources/validation/invalid_api_gw_out_event.json create mode 100644 powertools-e2e-tests/src/test/resources/validation/valid_alb_in_out_event.json create mode 100644 powertools-e2e-tests/src/test/resources/validation/valid_api_gw_in_out_event.json create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.java rename powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/{GenericSchemaV7Handler.java => GenericSchemaV7StringHandler.java} (92%) rename powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/{ValidationInboundStringHandler.java => ValidationInboundAPIGatewayV2HTTPEventHandler.java} (87%) create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/HandledResponseEventsArgumentsProvider.java diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 928ffb6c8..dfd97e0d4 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -156,7 +156,10 @@ We support JSON schema version 4, 6, 7 and 201909 (from [jmespath-jackson librar `@Validation` annotation is used to validate either inbound events or functions' response. -It will fail fast with `ValidationException` if an event or response doesn't conform with given JSON Schema. +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. +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. 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. diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 1bda3caa5..7c1208470 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -32,6 +32,7 @@ <module>metrics</module> <module>idempotency</module> <module>parameters</module> + <module>validation</module> </modules> <dependencyManagement> @@ -83,6 +84,11 @@ <artifactId>powertools-batch</artifactId> <version>${lambda.powertools.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-validation</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml new file mode 100644 index 000000000..31570fe4e --- /dev/null +++ b/powertools-e2e-tests/handlers/validation-alb-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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-validation-alb-event</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using Powertools for AWS Lambda (Java) validation</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-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..9129abc7d --- /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>1.0.0</version> + </parent> + + <artifactId>e2e-test-handler-validation-apigw-event</artifactId> + <packaging>jar</packaging> + <name>A Lambda function using Powertools for AWS Lambda (Java) validation</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/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..324c77a34 --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationALBE2ET.java @@ -0,0 +1,104 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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) + public 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 + public static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + + @Test + void test_validInboundSQSEvent() throws IOException { + InputStream inputStream = this.getClass().getResourceAsStream("/validation/valid_alb_in_out_event.json"); + String validEvent = IOUtils.toString(inputStream, 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 { + InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_alb_in_event.json"); + String invalidEvent = IOUtils.toString(inputStream, 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("$.price: is missing but it is required"); + } + + @Test + void test_invalidOutboundSQSEvent() throws IOException { + InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_alb_out_event.json"); + String invalidEvent = IOUtils.toString(inputStream, 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..af7c7d87c --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ValidationApiGWE2ET.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 ValidationApiGWE2ET { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static Infrastructure infrastructure; + private static String functionName; + + @BeforeAll + @Timeout(value = 5, unit = TimeUnit.MINUTES) + public 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 + public static void tearDown() { + if (infrastructure != null) { + infrastructure.destroy(); + } + } + + @Test + void test_validInboundApiGWEvent() throws IOException { + InputStream inputStream = this.getClass().getResourceAsStream("/validation/valid_api_gw_in_out_event.json"); + String validEvent = IOUtils.toString(inputStream, 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 { + InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_api_gw_in_event.json"); + String invalidEvent = IOUtils.toString(inputStream, 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("$.price: is missing but it is required"); + } + + @Test + void test_invalidOutboundApiGWEvent() throws IOException { + InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_api_gw_out_event.json"); + String invalidEvent = IOUtils.toString(inputStream, 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/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-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 0d71104f3..978be16de 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 @@ -22,6 +22,10 @@ import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; @@ -51,6 +55,7 @@ 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 @@ -73,6 +78,11 @@ public Object around(ProceedingJoinPoint pjp, if (validation.schemaVersion() != V201909) { 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 result = null; if (placedOnRequestHandler(pjp)) { validationNeeded = true; @@ -85,10 +95,10 @@ public Object around(ProceedingJoinPoint pjp, validate(obj, inboundJsonSchema, validation.envelope()); } else if (obj instanceof APIGatewayProxyRequestEvent) { APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) obj; - validate(event.getBody(), inboundJsonSchema); + result = validateAPIGatewayProxyBody(event.getBody(), inboundJsonSchema, null, null); } else if (obj instanceof APIGatewayV2HTTPEvent) { APIGatewayV2HTTPEvent event = (APIGatewayV2HTTPEvent) obj; - validate(event.getBody(), inboundJsonSchema); + result = validateAPIGatewayV2HTTPBody(event.getBody(), inboundJsonSchema, null, null); } else if (obj instanceof SNSEvent) { SNSEvent event = (SNSEvent) obj; event.getRecords().forEach(record -> validate(record.getSNS().getMessage(), inboundJsonSchema)); @@ -140,33 +150,100 @@ 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()); - } + // don't execute the lambda if result was set by previous validation step + // in that case result should already hold a response with validation information + if (result != null) { + LOG.error("Incoming API event's body failed inbound schema validation."); } + else { + result = pjp.proceed(proceedArgs); + + if (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; } + + /** + * 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 inboundJsonSchema 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 inboundJsonSchema 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/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.java new file mode 100644 index 000000000..74e8605a5 --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7APIGatewayProxyRequestEventHandler.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.handlers; + +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 GenericSchemaV7APIGatewayProxyRequestEventHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Validation(inboundSchema = "classpath:/schema_v7.json") + @Override + 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/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..1708ebeeb 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,31 @@ 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 java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; +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.ArgumentsSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + 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; @@ -40,25 +60,15 @@ import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; import com.networknt.schema.SpecVersion; -import java.io.IOException; -import java.util.stream.Stream; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.Signature; -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.ArgumentsSource; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; + 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.SQSWithCustomEnvelopeHandler; import software.amazon.lambda.powertools.validation.handlers.SQSWithWrongEnvelopeHandler; -import software.amazon.lambda.powertools.validation.handlers.ValidationInboundStringHandler; +import software.amazon.lambda.powertools.validation.handlers.ValidationInboundAPIGatewayV2HTTPEventHandler; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; @@ -99,7 +109,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,6 +124,47 @@ 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 { @@ -137,50 +188,84 @@ public void testValidateOutboundJsonSchema_APIGWV2() throws Throwable { @Test public void validate_inputOK_schemaInClasspath_shouldValidate() { - GenericSchemaV7Handler<APIGatewayProxyRequestEvent> handler = new GenericSchemaV7Handler(); + 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(); + 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(); + 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(); + 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); + + APIGatewayV2HTTPResponse response = handler.handleRequest(event, context); + assertThat(response.getBody()).isNotBlank(); + assertThat(response.getStatusCode()).isEqualTo(400); + assertThat(response.getHeaders()).isEmpty(); + assertThat(response.getMultiValueHeaders()).isEmpty(); } @Test @@ -189,7 +274,7 @@ public void validate_SQS() { LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); - GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); + GenericSchemaV7StringHandler<Object> handler = new GenericSchemaV7StringHandler<>(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } @@ -219,7 +304,7 @@ public void validate_Kinesis() { LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); KinesisEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/kinesis.json")); - GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); + GenericSchemaV7StringHandler<Object> handler = new GenericSchemaV7StringHandler<>(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); } @@ -229,7 +314,7 @@ public void validateEEvent(String jsonResource, Class eventClass) throws IOExcep 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"); } } From 00d04c3bd31b434b88a9a79732744a2511ff3932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:07:51 +0100 Subject: [PATCH 0538/1008] chore: fix end 2 end build (#1534) * different build on java8 * do not build example for artifact size --- .github/workflows/pr_artifacts_size.yml | 2 +- .github/workflows/run-e2e-tests.yml | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml index f37f83a8d..1d905af01 100644 --- a/.github/workflows/pr_artifacts_size.yml +++ b/.github/workflows/pr_artifacts_size.yml @@ -34,7 +34,7 @@ jobs: distribution: 'corretto' java-version: 11 - name: Build with Maven - run: mvn clean package --file pom.xml -DskipTests artifact:buildinfo + run: mvn clean package --file pom.xml -DskipTests artifact:buildinfo -pl '!software.amazon.lambda.examples:powertools-examples-idempotency,!software.amazon.lambda.examples:powertools-examples-batch,!software.amazon.lambda.examples:powertools-examples-cloudformation,!software.amazon.lambda.examples:powertools-examples-core-utilities-cdk,!software.amazon.lambda.examples:powertools-examples-core-utilities-sam,!software.amazon.lambda.examples:powertools-examples-core-utilities-serverless,!software.amazon.lambda.examples:powertools-examples-core-utilities-terraform,!software.amazon.lambda.examples:powertools-examples-parameters,!software.amazon.lambda.examples:powertools-examples-serialization,!software.amazon.lambda.examples:powertools-examples-validation,!software.amazon.lambda.examples:cdk,!software.amazon.lambda:powertools-examples' - name: Get artifacts size & build report id: artifacts-size-report run: | diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 60048abdc..5c7bc0b71 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -56,5 +56,12 @@ jobs: with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} + + - name: Build with Maven Java 8 + if: ${{ matrix.java == '8' }} # If 8 exclude the examples directory + run: mvn -DskipTests install --file pom.xml -pl '!software.amazon.lambda.examples:powertools-examples-idempotency,!software.amazon.lambda.examples:powertools-examples-batch,!software.amazon.lambda.examples:powertools-examples-cloudformation,!software.amazon.lambda.examples:powertools-examples-core-utilities-cdk,!software.amazon.lambda.examples:powertools-examples-core-utilities-sam,!software.amazon.lambda.examples:powertools-examples-core-utilities-serverless,!software.amazon.lambda.examples:powertools-examples-core-utilities-terraform,!software.amazon.lambda.examples:powertools-examples-parameters,!software.amazon.lambda.examples:powertools-examples-serialization,!software.amazon.lambda.examples:powertools-examples-validation,!software.amazon.lambda.examples:cdk,!software.amazon.lambda:powertools-examples' + - name: Build with Maven + if: ${{ matrix.java != '8' }} # If not 8 don't exclude the examples directory + run: mvn -DskipTests install --file pom.xml - 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 + run: mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file From 03024bec2805cb1b86b51c35e1d8e43998091ff4 Mon Sep 17 00:00:00 2001 From: Jason Harris <jiharris90@gmail.com> Date: Tue, 5 Dec 2023 08:33:19 +0000 Subject: [PATCH 0539/1008] docs: HelloWorldStreamFunction in examples fails with sam (#1532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Setting up Kotlin environment. Converting test to Kotlin. * Deploying via SAM successfully. * Added Kotlin example. * Removing unused Gradle build file. * Adding SAM template so can be used as an existing project and Java target compatibility * Adding SAM template so can be used as an existing project * Updating guidance to use SAM for build and deploy * Restructuring separate Java and Kotlin examples. * Updating core examples readme to represent new structure for Java and Kotlin examples. * Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive * Updating to fix trailing \n * Updating guidance to be more specific for examples * Adopting new mechanism for specifying jvm target. * accommodating new project structure * Fixing link typo after refactoring * Setting up Kotlin environment. Converting test to Kotlin. * Deploying via SAM successfully. * Added Kotlin example. * Removing unused Gradle build file. * Adding SAM template so can be used as an existing project and Java target compatibility * Adding SAM template so can be used as an existing project * Updating guidance to use SAM for build and deploy * Restructuring separate Java and Kotlin examples. * Updating core examples readme to represent new structure for Java and Kotlin examples. * Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive * Updating to fix trailing \n * Updating guidance to be more specific for examples * Adopting new mechanism for specifying jvm target. * accommodating new project structure * Fixing link typo after refactoring * Flattening structure back to original to make merging easier for v2 * Adding build for Kotlin Gradle * Adding build for Kotlin Gradle - Restructuring Java examples to v1 approach * Correcting paths * Adding SNAPSHOT support and local capability for Maven. Testing using Java 1.8 * Reviewed and updated against PR comments. * Un-commenting examples * Adding validation step for IaC SAM * Adding Terraform for Java projects IaC validator and linter * Adding additional projects for SAM validation and matrix approach * Refactoring stream function to process input logging example with a Lambda Function URL instead of APIGW. * Demonstrating Java streaming response * Refactoring stream function to process input logging example to return * Update CONTRIBUTING.md * fix: get trace id from system property when env var is not set (#1503) * fix: check if XRAY Trace ID is present in System property * chore: remove erroneous extra char in tests * fix #1500 (#1506) * feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) * chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) * feat: ALC (#1514) * handle AWS_LAMBDA_LOG configuration * ALC documentation + code review * update doc * chore:Prep release 1.18.0 (#1515) * chore:prep release 1.18.0 * update version * update version in kotlin example * maven local repo in gradle example * update changelog --------- Co-authored-by: scottgerring <scottgerring@users.noreply.github.com> * chore: update version to next snapshot: 1-19.0-SNAPSHOT (#1516) * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * building only for LTS * Add some more margin to the test pause (#1518) * test: e2e tests with java 21 (#1517) * e2e tests with java 21 * Run Java21 tests using the Java17 compiler * Run all of the E2E tests in parallel, not just the first 3 * Try again * . * Let's try again * Add some comment on Java21 to the repo * Add caveat about lambda runtimes * Clean up wording a little --------- Co-authored-by: Scott Gerring <gerrings@amazon.com> * update doc for ALC (#1520) * chore: Testing java21 aspectj pre-release (#1519) * e2e tests with java 21 * use aspectj 1.9.21-SNAPSHOT * Fix log4j2.xml missing in logging test for java21 * rollback double runtime * remove comment * keep aspectj 1.9.7 in parent for java8 compatibility * use M1 instead of snapshot * update documentation for aspectj * update documentation for aspectj --------- Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> * chore: Remove build cruft * Adding context for using RequestStreamHandler * removing pr_lint * Update examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java Clarify usage of RequestStreamHandler. Co-authored-by: Alexey Soshin <alexey.soshin@gmail.com> --------- Co-authored-by: Jason Harris <harrzjas@amazon.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> Co-authored-by: Jason Harris <harrzjas@amazon.co.uk> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> Co-authored-by: Michele Ricciardi <mriccia@amazon.com> Co-authored-by: Alexey Soshin <alexey.soshin@gmail.com> Co-authored-by: jdoherty <jdoherty07@gmail.com> Co-authored-by: Scott Gerring <gerrings@amazon.com> Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> --- .../src/main/java/helloworld/AppStream.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java index 401ef8c48..94806cc38 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java @@ -17,22 +17,43 @@ 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.util.Map; +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.Metrics; +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) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); + // 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)))) { - System.out.println(map.size()); + log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + + writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); + } catch (IOException e) { + log.error("Something has gone wrong: ", e); + } } } + From 04d692a54b29a8dd40301153ba938c937ff0a3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:39:47 +0100 Subject: [PATCH 0540/1008] deps: bump aspectj to 1.9.21 for jdk21 (#1536) --- docs/index.md | 3 --- powertools-e2e-tests/handlers/pom.xml | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/index.md b/docs/index.md index 884e02476..06c9beb6d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -306,9 +306,6 @@ Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj | `11-17` | `1.9.20.1` | | `21` | `1.9.21` | -_Note: 1.9.21 is not yet available and Java 21 not yet officially supported by aspectj, but you can already use the `1.9.21.M1`_ - - ## Environment variables !!! info diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index a9096477b..799ed465e 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -205,7 +205,7 @@ <jdk>[21,)</jdk> </activation> <properties> - <aspectj.version>1.9.21.M1</aspectj.version> + <aspectj.version>1.9.21</aspectj.version> </properties> </profile> </profiles> From 6b0fe5a19eda5be0c8d4890f1f39faf8a61453cd Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:13:16 +0100 Subject: [PATCH 0541/1008] chore(v2): Split parameters module up by parameter provider (#1403) --- docs/utilities/parameters.md | 693 +++++++++--------- .../powertools-examples-parameters/pom.xml | 8 +- .../demo/parameters/ParametersFunction.java | 25 +- mkdocs.yml | 2 +- pom.xml | 34 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 4 +- .../handlers/parameters/pom.xml | 6 +- .../lambda/powertools/e2e/Function.java | 10 +- powertools-e2e-tests/handlers/pom.xml | 10 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- .../handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-parameters/pom.xml | 62 +- .../powertools-parameters-appconfig/pom.xml | 85 +++ .../parameters/appconfig/AppConfigParam.java | 57 ++ .../appconfig/AppConfigParametersAspect.java | 54 ++ .../appconfig}/AppConfigProvider.java | 120 +-- .../appconfig/AppConfigProviderBuilder.java | 124 ++++ .../appconfig/AppConfigParamAspectTest.java | 55 ++ .../appconfig}/AppConfigProviderTest.java | 16 +- .../powertools-parameters-dynamodb/pom.xml | 85 +++ .../parameters/dynamodb/DynamoDbParam.java} | 34 +- .../dynamodb/DynamoDbParamAspect.java | 51 ++ .../parameters/dynamodb/DynamoDbProvider.java | 121 +++ .../dynamodb/DynamoDbProviderBuilder.java | 113 +++ .../DynamoDbProviderSchemaException.java | 2 +- .../dynamodb/DynamoDbParamAspectTest.java | 55 ++ .../dynamodb}/DynamoDbProviderE2ETest.java | 4 +- .../dynamodb}/DynamoDbProviderTest.java | 15 +- .../powertools-parameters-secrets/pom.xml | 85 +++ .../parameters/secrets/SecretsParam.java | 46 ++ .../secrets/SecretsParamAspect.java | 48 ++ .../parameters/secrets/SecretsProvider.java | 115 +++ .../secrets/SecretsProviderBuilder.java | 101 +++ .../secrets/SecretsParamAspectTest.java | 49 ++ .../secrets}/SecretsProviderTest.java | 26 +- .../powertools-parameters-ssm/pom.xml | 85 +++ .../powertools/parameters/ssm/SSMParam.java | 47 ++ .../parameters/ssm/SSMParamAspect.java | 49 ++ .../parameters/ssm}/SSMProvider.java | 140 +--- .../parameters/ssm/SSMProviderBuilder.java | 98 +++ .../parameters/ssm/SSMParamAspectTest.java | 52 ++ .../parameters/ssm}/SSMProviderTest.java | 40 +- .../powertools-parameters-tests/pom.xml | 67 ++ .../parameters/BaseProviderTest.java | 47 +- .../ParamProvidersIntegrationTest.java} | 43 +- .../parameters/cache/CacheManagerTest.java | 0 .../parameters/cache/DataStoreTest.java | 0 .../parameters/internal/AnotherObject.java | 0 .../parameters/internal/CustomProvider.java | 2 +- .../transform/Base64TransformerTest.java | 0 .../transform/JsonTransformerTest.java | 0 .../transform/ObjectToDeserialize.java | 0 .../transform/TransformationManagerTest.java | 0 powertools-parameters/spotbugs-exclude.xml | 82 +++ .../parameters/BaseParamAspect.java | 57 ++ .../powertools/parameters/BaseProvider.java | 26 +- .../parameters/DynamoDbProvider.java | 213 ------ .../powertools/parameters/ParamManager.java | 194 ----- .../parameters/SecretsProvider.java | 226 ------ .../internal/LambdaParametersAspect.java | 56 -- .../parameters/ParamManagerTest.java | 118 --- .../internal/LambdaParametersAspectTest.java | 105 --- spotbugs-exclude.xml | 50 +- 69 files changed, 2363 insertions(+), 1765 deletions(-) create mode 100644 powertools-parameters/powertools-parameters-appconfig/pom.xml create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParam.java create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java rename powertools-parameters/{src/main/java/software/amazon/lambda/powertools/parameters => powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig}/AppConfigProvider.java (52%) create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java rename powertools-parameters/{src/test/java/software/amazon/lambda/powertools/parameters => powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig}/AppConfigProviderTest.java (95%) create mode 100644 powertools-parameters/powertools-parameters-dynamodb/pom.xml rename powertools-parameters/{src/main/java/software/amazon/lambda/powertools/parameters/Param.java => powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParam.java} (52%) create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProvider.java create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderBuilder.java rename powertools-parameters/{src/main/java/software/amazon/lambda/powertools/parameters => powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb}/exception/DynamoDbProviderSchemaException.java (92%) create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java rename powertools-parameters/{src/test/java/software/amazon/lambda/powertools/parameters => powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb}/DynamoDbProviderE2ETest.java (96%) rename powertools-parameters/{src/test/java/software/amazon/lambda/powertools/parameters => powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb}/DynamoDbProviderTest.java (94%) create mode 100644 powertools-parameters/powertools-parameters-secrets/pom.xml create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParam.java create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProvider.java create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderBuilder.java create mode 100644 powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java rename powertools-parameters/{src/test/java/software/amazon/lambda/powertools/parameters => powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets}/SecretsProviderTest.java (81%) create mode 100644 powertools-parameters/powertools-parameters-ssm/pom.xml create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParam.java create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java rename powertools-parameters/{src/main/java/software/amazon/lambda/powertools/parameters => powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm}/SSMProvider.java (60%) create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java create mode 100644 powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java rename powertools-parameters/{src/test/java/software/amazon/lambda/powertools/parameters => powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm}/SSMProviderTest.java (84%) create mode 100644 powertools-parameters/powertools-parameters-tests/pom.xml rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java (91%) rename powertools-parameters/{src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java => powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java} (81%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java (100%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java (100%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java (100%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java (97%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java (100%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java (100%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java (100%) rename powertools-parameters/{ => powertools-parameters-tests}/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java (100%) create mode 100644 powertools-parameters/spotbugs-exclude.xml create mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java delete mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java delete mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java delete mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java delete mode 100644 powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java delete mode 100644 powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java delete mode 100644 powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 85d30d77e..f72c7704e 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -4,29 +4,46 @@ 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 ## 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` + +You can easily mix and match parameter providers within the same project for different needs. + +Depending on which Java version you are using, you configuration will differ. Note that you must also provide +the concrete parameters module you want to use below - see the TODOs! === "Maven Java 11+" - ```xml hl_lines="3-7 16 18 24-27" + ```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> @@ -44,9 +61,10 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl <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> @@ -65,13 +83,19 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "Maven Java 1.8" - ```xml hl_lines="3-7 16 18 24-27" + ```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> @@ -89,9 +113,10 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl <target>1.8</target> <complianceLevel>1.8</complianceLevel> <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> @@ -110,7 +135,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "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' @@ -121,7 +146,8 @@ 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 }}' } sourceCompatibility = 11 // or higher @@ -130,7 +156,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl === "Gradle Java 1.8" - ```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' @@ -140,8 +166,9 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl mavenCentral() } + // TODO! Provide an aspectLibrary for each of the parameters module(s) you want to use here dependencies { - aspect 'software.amazon.lambda:powertools-parameters:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' } sourceCompatibility = 1.8 @@ -152,63 +179,78 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl 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 either using annotations or by using the `xParamProvider` class for each parameter +provider directly. The latter is useful if you need to configure the underlying SDK client, for example to use +a different region or credentials, the former is simpler to use. -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. +## Built-in provider classes -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. +This section describes the built-in provider classes for each parameter store, providing +examples showing how to inject parameters using annotations, and how to use the provider +interface. In cases where a provider supports extra features, these will also be described. -=== "SSMProvider" +### Secrets Manager - ```java hl_lines="6" - import software.amazon.lambda.powertools.parameters.SSMProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; +=== "Secrets Manager: @SecretsParam" - 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"); + ```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" +=== "Secrets Manager: SecretsProvider" - ```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,9 +259,59 @@ 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" +=== "SSM Parameter Store: @SSMParam" + + ```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; + + 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 + } + } + ``` + +=== "SSM Parameter Store: SSMProvider" + + ```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; + } + } + ``` + +=== "SSM Parameter Store: Additional Options" ```java hl_lines="9 12" import software.amazon.lambda.powertools.parameters.SSMProvider; @@ -238,183 +330,159 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -## Secrets Manager +### DynamoDB -For secrets stored in Secrets Manager, use `getSecretsProvider`. +=== "DynamoDB: @DyanmoDbParam" -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. + ```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; + public class ParametersFunction implements RequestHandler<String, String> { -=== "SecretsProvider" + // Annotation-style injection from DynamoDB + @DynamoDbParam(table = "my-test-tablename", key = "myKey") + String ddbParam; - ```java hl_lines="9" - import software.amazon.lambda.powertools.parameters.SecretsProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; - - 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" +=== "DynamoDB: DynamoDbProvider" - ```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> { - // Retrieve a single secret - String value = secretsProvider.get("/my/secret"); + // 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(); + 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" +=== "AppConfig: @AppConfigParam" - ```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" +=== "AppConfig: 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(); - // Retrieve a single parameter - String value = appConfigProvider.get("my-key"); - } + public String handleRequest(String input, Context context) { + // Retrieve a single param + String value = appConfigProvider + .get("/my/secret"); + + // 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 +499,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 +540,141 @@ 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 -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. +### Create your own Provider +You can create your own custom parameter store provider by implementing a handful of classes: -=== "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/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 7a4af628c..8d7fbd4f4 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -21,9 +21,15 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> + <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> diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java index 5b691cfd9..9a1d3636b 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java @@ -31,17 +31,30 @@ 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 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); - 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/mkdocs.yml b/mkdocs.yml index a8569f664..b7f793e18 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,7 +85,7 @@ extra_javascript: extra: powertools: - version: 1.18.0 # to update after each release (we do not want snapshot version here) + version: 2.0.0 # to update after each release (we do not want snapshot version here) repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index cad72dee1..1d35a990f 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,11 @@ <module>powertools-e2e-tests</module> <module>powertools-batch</module> <module>examples</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> </modules> <scm> @@ -600,35 +605,6 @@ </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.3</version> - </dependency> - </dependencies> - <executions> - <execution> - <goals> - <goal>check</goal> - </goals> - </execution> - </executions> - </plugin> </plugins> </build> </profile> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index aee9bf3dd..8740dcb0b 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 22b6a1c53..a0e6bd4fc 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 8302624ef..277e76fc1 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 1fe9092ef..d887341c5 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 05e91d8e3..222c5ab2e 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-logging</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 2f3cabd16..68059e67e 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/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.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) Parameters</name> + <name>A Lambda function using Powertools for AWS Lambda (Java) Metrics</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 4d5330da0..328d30485 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/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.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools logging</name> + <name>A Lambda function using powertools parameters</name> <dependencies> <dependency> @@ -19,7 +19,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> + <artifactId>powertools-parameters-appconfig</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> 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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7c1208470..2c73de977 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> @@ -27,12 +27,16 @@ </properties> <modules> + <module>batch</module> + <module>largemessage</module> + <module>largemessage_idempotent</module> <module>logging</module> <module>tracing</module> <module>metrics</module> <module>idempotency</module> <module>parameters</module> - <module>validation</module> + <module>validation-alb-event</module> + <module>validation-apigw-event</module> </modules> <dependencyManagement> @@ -71,7 +75,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> + <artifactId>powertools-parameters-appconfig</artifactId> <version>${lambda.powertools.version}</version> </dependency> <dependency> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index b9240c356..b96fcef0a 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index 31570fe4e..be50094c1 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 9129abc7d..f204a8a9f 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>1.0.0</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index eb73962e1..1bc662457 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -28,77 +28,17 @@ <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> + <description>Set of utilities to retrieve parameters - common interface</description> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</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> - </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>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> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml new file mode 100644 index 000000000..3a3018c7c --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -0,0 +1,85 @@ +<?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.0.0-SNAPSHOT</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> + + <!-- 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.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <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> +</project> \ No newline at end of file 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/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java similarity index 52% rename from powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java rename to powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java index 5a1e575dd..5fd272c9b 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java @@ -12,20 +12,16 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.appconfig; 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.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; @@ -50,8 +46,9 @@ public class AppConfigProvider extends BaseProvider { private final String environment; private final HashMap<String, EstablishedSession> establishedSessions = new HashMap<>(); - AppConfigProvider(CacheManager cacheManager, AppConfigDataClient client, String environment, String application) { - super(cacheManager); + AppConfigProvider(CacheManager cacheManager, TransformationManager transformationManager, + AppConfigDataClient client, String environment, String application) { + super(cacheManager, transformationManager); this.client = client; this.application = application; this.environment = environment; @@ -60,12 +57,13 @@ public class AppConfigProvider extends BaseProvider { /** * Create a builder that can be used to configure and create a {@link AppConfigProvider}. * - * @return a new instance of {@link AppConfigProvider.Builder} + * @return a new instance of {@link AppConfigProviderBuilder} */ - public static AppConfigProvider.Builder builder() { - return new AppConfigProvider.Builder(); + public static AppConfigProviderBuilder builder() { + return new AppConfigProviderBuilder(); } + /** * Retrieve the parameter value from the AppConfig parameter store.<br /> * @@ -128,102 +126,4 @@ private EstablishedSession(String nextSessionToken, String 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/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..dadacb843 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java @@ -0,0 +1,124 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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"); + } + + // 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/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..f50e88ec5 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.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.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; + +public class AppConfigParamAspectTest { + + @Test + public 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 95% 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..ded568e8d 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,11 +12,11 @@ * */ -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.assertj.core.api.Assertions.assertThat; import static org.mockito.MockitoAnnotations.openMocks; import org.junit.jupiter.api.BeforeEach; @@ -193,18 +193,6 @@ public void getMultipleValuesThrowsException() { .withMessage("Retrieving multiple parameter values is not supported with the AWS App Config Provider"); } - @Test - public void testAppConfigProviderBuilderMissingCacheManager_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() - .withEnvironment(environmentName) - .withApplication(applicationName) - .withClient(client) - .build()) - .withMessage("No CacheManager provided; please provide one"); - } - @Test public void testAppConfigProviderBuilderMissingEnvironment_throwsException() { diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml new file mode 100644 index 000000000..ad32bdff8 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -0,0 +1,85 @@ +<?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.0.0-SNAPSHOT</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> + + <!-- 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.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <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> +</project> \ No newline at end of file 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..6b6610ba1 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderBuilder.java @@ -0,0 +1,113 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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(); + } + 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/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..07e93a7c1 --- /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; + +public class DynamoDbParamAspectTest { + + @Test + public 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 96% 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..2695938d8 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; @@ -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 94% 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..68bfd7cdb 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,7 +12,7 @@ * */ -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; @@ -35,7 +35,7 @@ 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 { @@ -55,7 +55,7 @@ public class DynamoDbProviderTest { public void init() { openMocks(this); CacheManager cacheManager = new CacheManager(); - provider = new DynamoDbProvider(cacheManager, client, tableName); + provider = new DynamoDbProvider(cacheManager, transformationManager, client, tableName); } @@ -217,15 +217,6 @@ public void getValuesWithMalformedRowThrows() { }); } - @Test - public void testDynamoDBBuilderMissingCacheManager_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() - .withTable("table") - .build()); - } - @Test public void testDynamoDBBuilderMissingTable_throwsException() { diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml new file mode 100644 index 000000000..6c4501ca3 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -0,0 +1,85 @@ +<?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.0.0-SNAPSHOT</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> + + <!-- 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.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <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> +</project> \ No newline at end of file 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..125425200 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderBuilder.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.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) { + // TODO - what should we do with this + cacheManager = new CacheManager(); + } + SecretsProvider provider; + if (client == null) { + client = createClient(); + } + + 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/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..7aa0f0872 --- /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; + +public class SecretsParamAspectTest { + + @Test + public 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 81% 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..21173cad1 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,12 +12,11 @@ * */ -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.assertNotNull; import java.time.temporal.ChronoUnit; import java.util.Base64; @@ -27,6 +26,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; @@ -51,9 +51,9 @@ public class SecretsProviderTest { @BeforeEach public void init() { - openMocks(this); + MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); - provider = new SecretsProvider(cacheManager, client); + provider = new SecretsProvider(cacheManager, transformationManager, client); } @Test @@ -62,7 +62,6 @@ public void getValue() { 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); @@ -96,13 +95,14 @@ public void getMultipleValuesThrowsException() { } @Test - public void testSecretsProviderBuilderMissingCacheManager_throwsException() { + public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> SecretsProvider.builder() - .withClient(client) - .withTransformationManager(transformationManager) - .build()) - .withMessage("No CacheManager provided, please provide one"); + // Act + SecretsProvider secretsProvider = SecretsProvider.builder() + .build(); + + // Assert + assertNotNull(secretsProvider); + assertNotNull(secretsProvider.getClient()); } } diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml new file mode 100644 index 000000000..80276189d --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -0,0 +1,85 @@ +<?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.0.0-SNAPSHOT</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> + + <!-- 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.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <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> +</project> \ No newline at end of file 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 60% 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 10bb70c15..c24b08b84 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.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; -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); @@ -80,33 +73,30 @@ public class SSMProvider extends BaseProvider { * 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(); } /** @@ -124,33 +114,6 @@ public String getValue(String key) { 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/> @@ -210,7 +173,7 @@ private Map<String, String> getMultipleBis(String path, String nextToken) { res.parameters().forEach(parameter -> { /* Standardize the parameter name - The parameter name returned by SSM will contained the full path. + The parameter name returned by SSM will contain the full path. However, for readability, we should return only the part after the path. */ @@ -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..3b4fff1b3 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java @@ -0,0 +1,98 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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(); + } + + 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/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..e56d20ffa --- /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; + +public class SSMParamAspectTest { + + // This class tests the SSM Param aspect in the same fashion + // as the tests for the aspects for the other providers. + + @Test + public 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 84% 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..b105da438 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,16 +12,10 @@ * */ -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 java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -31,8 +25,11 @@ 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; @@ -62,9 +59,9 @@ public class SSMProviderTest { @BeforeEach public void init() { - openMocks(this); + MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); - provider = new SSMProvider(cacheManager, client); + provider = new SSMProvider(cacheManager, null, client); } @Test @@ -100,7 +97,7 @@ public void getMultiple() { 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( @@ -123,7 +120,7 @@ public void getMultipleWithTrailingSlash() { 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( @@ -146,7 +143,7 @@ public void getMultiple_cached_shouldNotCallSSM() { 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,7 +153,8 @@ 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)); } @@ -172,7 +170,7 @@ public void getMultipleWithNextToken() { 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"); @@ -196,22 +194,10 @@ public void getMultipleWithNextToken() { 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"); - } - 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-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml new file mode 100644 index 000000000..d8e9b2a02 --- /dev/null +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -0,0 +1,67 @@ +<?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.0.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>powertools-parameters-tests</artifactId> + <description>Powertools parameters tests that cut across all the parameters providers</description> + <properties> + <!-- Don't deploy the tests --> + <maven.deploy.skip>true</maven.deploy.skip> + </properties> + + <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.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> +</project> \ No newline at end of file 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 91% 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..5fa740253 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 @@ -25,6 +25,7 @@ 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; @@ -51,9 +52,8 @@ public void setup() { clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); - provider = new BasicProvider(cacheManager); transformationManager = new TransformationManager(); - provider.setTransformationManager(transformationManager); + provider = new BasicProvider(cacheManager, transformationManager); } @Test @@ -138,7 +138,8 @@ public void get_customTTL_expired_shouldGetValue() { @Test public void get_customDefaultTTL_cached_shouldGetFromCache() { - provider.defaultMaxAge(12, ChronoUnit.MINUTES).get("foobar"); + provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); + provider.get("foobar"); getFromStore = false; provider.setClock(offset(clock, of(10, MINUTES))); @@ -149,7 +150,7 @@ public void get_customDefaultTTL_cached_shouldGetFromCache() { @Test public void get_customDefaultTTL_expired_shouldGetValue() { - provider.defaultMaxAge(2, ChronoUnit.MINUTES).get("barbaz"); + provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); getFromStore = false; provider.setClock(offset(clock, of(3, MINUTES))); @@ -160,9 +161,7 @@ public void get_customDefaultTTL_expired_shouldGetValue() { @Test public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { - provider.defaultMaxAge(12, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) - .get("foobaz"); + provider.get("foobaz"); getFromStore = false; provider.setClock(offset(clock, of(4, SECONDS))); @@ -173,9 +172,10 @@ public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { @Test public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) - .get("bariton"); + + provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); + + provider.withMaxAge(5, SECONDS).get("bariton"); getFromStore = false; provider.setClock(offset(clock, of(6, SECONDS))); @@ -275,8 +275,9 @@ public void getObject_customTTL_expired_shouldGetValue() { public 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; @@ -290,8 +291,9 @@ public void getObject_customDefaultTTL_cached_shouldGetFromCache() { public 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; @@ -305,9 +307,10 @@ public void getObject_customDefaultTTL_expired_shouldGetValue() { public 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; @@ -321,8 +324,9 @@ public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { public 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; @@ -335,7 +339,7 @@ public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { @Test public void get_noTransformationManager_shouldThrowException() { - provider.setTransformationManager(null); + provider = new BasicProvider(new CacheManager(), null); assertThatIllegalStateException() .isThrownBy(() -> provider.withTransformation(base64).get("foo")); @@ -343,7 +347,6 @@ public void get_noTransformationManager_shouldThrowException() { @Test public void getObject_noTransformationManager_shouldThrowException() { - provider.setTransformationManager(null); assertThatIllegalStateException() .isThrownBy(() -> provider.get("foo", ObjectToDeserialize.class)); @@ -379,8 +382,8 @@ 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) { 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 81% 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..7d790d140 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,7 +14,6 @@ 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; @@ -25,14 +24,12 @@ 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.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -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.secretsmanager.model.GetSecretValueRequest; @@ -43,8 +40,10 @@ 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 { +public class ParamProvidersIntegrationTest { @Mock SsmClient ssmClient; @@ -59,19 +58,17 @@ public class ParamManagerIntegrationTest { 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); + SSMProvider ssmProvider = SSMProvider.builder() + .withClient(ssmClient) + .build(); String expectedValue = "value"; Parameter parameter = Parameter.builder().value(expectedValue).build(); @@ -87,7 +84,9 @@ public void ssmProvider_get() { @Test public void ssmProvider_getMultiple() { - SSMProvider ssmProvider = ParamManager.getSsmProvider(ssmClient); + SSMProvider ssmProvider = SSMProvider.builder() + .withClient(ssmClient) + .build(); List<Parameter> parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); @@ -112,7 +111,9 @@ public void ssmProvider_getMultiple() { @Test public void secretsProvider_get() { - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(secretsManagerClient); + SecretsProvider secretsProvider = SecretsProvider.builder() + .withClient(secretsManagerClient) + .build(); String expectedValue = "Value1"; GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(expectedValue).build(); @@ -125,24 +126,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/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 similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java 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 100% 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 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 100% 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 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 100% 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 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/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 similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java diff --git a/powertools-parameters/spotbugs-exclude.xml b/powertools-parameters/spotbugs-exclude.xml new file mode 100644 index 000000000..d48e9bee1 --- /dev/null +++ b/powertools-parameters/spotbugs-exclude.xml @@ -0,0 +1,82 @@ +<!-- 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="EI_EXPOSE_REP2"/> + <Or> + <And> + <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.parameters.transform.TransformationManager"/> + <Field name="transformation"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.BaseProvider"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="cacheManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="client"/> + </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.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.parameters.dynamodb.DynamoDbProviderBuilder"/> + <Field name="cacheManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> + <Field name="transformationManager"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> + <Field name="client"/> + </And> + + </Or> + </Match> +</FindBugsFilter> \ No newline at end of file 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..bedace28c 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 @@ -34,11 +34,12 @@ 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 +60,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 @@ -225,10 +211,6 @@ protected void resetToDefaults() { } } - 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 363f39d7c..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.common.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 b77f501f2..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.common.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/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/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/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/spotbugs-exclude.xml b/spotbugs-exclude.xml index eca7e266f..dc22f22b6 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -8,6 +8,40 @@ https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html --> <FindBugsFilter> + <Match> + <Bug pattern="EI_EXPOSE_REP2"/> + <Or> + <And> + <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.parameters.transform.TransformationManager"/> + <Field name="transformation"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> + <Field name="transformationManager"/> + </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> + + </Or> + </Match> <!-- Internals of Log event for apache log4j--> <Match> <Bug pattern="EI_EXPOSE_REP"/> @@ -65,14 +99,6 @@ <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"/> @@ -140,18 +166,10 @@ <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"/> - </And> </Or> </Match> <!--False positive https://github.com/spotbugs/spotbugs/issues/1539--> From b139a96b45101830bb44e1854c4e96552cdfbc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:41:48 +0100 Subject: [PATCH 0542/1008] feat(v2): new logging module (#1435) --- docs/core/logging.md | 1134 ++++++++++++----- docs/stylesheets/extra.css | 2 +- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 7 +- .../dynamo/DynamoDBStreamBatchHandler.java | 6 +- .../org/demo/batch/dynamo/DynamoDBWriter.java | 6 +- .../batch/kinesis/KinesisBatchHandler.java | 6 +- .../batch/kinesis/KinesisBatchSender.java | 6 +- .../org/demo/batch/sqs/SqsBatchHandler.java | 6 +- .../org/demo/batch/sqs/SqsBatchSender.java | 6 +- .../pom.xml | 25 +- .../src/main/java/helloworld/App.java | 20 +- .../cdk/app/pom.xml | 4 +- .../cdk/app/src/main/java/helloworld/App.java | 7 +- .../gradle/build.gradle | 3 +- .../gradle/src/main/java/helloworld/App.java | 6 +- .../kotlin/build.gradle.kts | 16 +- .../kotlin/src/main/kotlin/helloworld/App.kt | 5 +- .../src/test/kotlin/helloworld/AppTest.kt | 20 - .../sam/pom.xml | 17 +- .../sam/src/main/java/helloworld/App.java | 6 +- .../serverless/pom.xml | 17 +- .../terraform/pom.xml | 17 +- .../powertools-examples-idempotency/pom.xml | 22 +- .../src/main/java/helloworld/App.java | 6 +- .../powertools-examples-parameters/pom.xml | 6 +- .../demo/parameters/ParametersFunction.java | 6 +- .../powertools-examples-serialization/pom.xml | 4 +- ...GatewayRequestDeserializationFunction.java | 13 +- .../SQSEventDeserializationFunction.java | 6 +- .../powertools-examples-validation/pom.xml | 6 +- pom.xml | 64 +- powertools-batch/pom.xml | 2 +- .../handler/DynamoDbBatchMessageHandler.java | 2 +- .../KinesisStreamsBatchMessageHandler.java | 2 +- .../batch/handler/SqsBatchMessageHandler.java | 4 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 12 +- .../common/internal/LambdaConstants.java | 6 - .../internal/LambdaHandlerProcessor.java | 1 - .../internal/UserAgentConfigurator.java | 6 +- .../internal/LambdaHandlerProcessorTest.java | 3 - powertools-e2e-tests/handlers/batch/pom.xml | 6 +- .../lambda/powertools/e2e/Function.java | 24 +- .../handlers/idempotency/pom.xml | 6 +- .../handlers/largemessage/pom.xml | 6 +- .../handlers/largemessage_idempotent/pom.xml | 6 +- powertools-e2e-tests/handlers/logging/pom.xml | 11 +- .../lambda/powertools/e2e/Function.java | 8 +- .../handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 13 +- powertools-e2e-tests/pom.xml | 3 +- .../amazon/lambda/powertools/LoggingE2ET.java | 4 +- powertools-idempotency/pom.xml | 11 +- .../powertools/idempotency/Idempotency.java | 2 +- .../handlers/IdempotencyFunction.java | 6 +- powertools-large-messages/pom.xml | 7 +- powertools-logging/pom.xml | 55 +- .../powertools-logging-log4j/pom.xml | 137 ++ .../json/resolver/PowertoolsResolver.java | 286 +++++ .../resolver}/PowertoolsResolverFactory.java | 15 +- .../log4/internal/Log4jLoggingManager.java | 48 + .../src/main/resources/LambdaEcsLayout.json | 89 ++ .../src/main/resources/LambdaJsonLayout.json | 72 ++ ...powertools.logging.internal.LoggingManager | 1 + .../PowerToolsResolverFactoryTest.java | 95 ++ .../PowertoolsMessageResolverTest.java | 115 ++ .../json/resolver/PowertoolsResolverTest.java | 110 ++ .../internal/Log4jLoggingManagerTest.java | 51 + .../handler/PowertoolsJsonMessage.java | 47 + .../handler/PowertoolsLogEnabled.java | 34 + .../test/resources/junit-platform.properties | 17 + .../src/test/resources/log4j2.xml | 24 + .../powertools-logging-logback/pom.xml | 137 ++ .../logging/logback/LambdaEcsEncoder.java | 199 +++ .../logging/logback/LambdaJsonEncoder.java | 208 +++ .../logging/logback/internal/JsonUtils.java | 130 ++ .../logback/internal/LambdaEcsSerializer.java | 187 +++ .../internal/LambdaJsonSerializer.java | 150 +++ .../internal/LogbackLoggingManager.java | 59 + ...powertools.logging.internal.LoggingManager | 1 + .../logging/LogbackLoggingManagerTest.java | 54 + .../internal/LambdaEcsEncoderTest.java | 176 +++ .../internal/LambdaJsonEncoderTest.java | 263 ++++ .../handler/PowertoolsJsonMessage.java | 44 + .../handler/PowertoolsLogEnabled.java | 34 + .../test/resources/junit-platform.properties | 17 + .../src/test/resources/logback-test.xml | 27 + powertools-logging/spotbugs-exclude.xml | 30 + ...Constants.java => CorrelationIdPaths.java} | 14 +- .../lambda/powertools/logging/Logging.java | 37 +- .../powertools/logging/LoggingUtils.java | 56 +- .../internal/AbstractJacksonLayoutCopy.java | 519 -------- .../internal/DefautlLoggingManager.java | 35 + .../logging/internal/JacksonFactoryCopy.java | 133 -- .../logging/internal/LambdaJsonLayout.java | 246 ---- .../logging/internal/LambdaLoggingAspect.java | 372 ++++-- .../internal/LambdaTimestampResolver.java | 169 --- .../LambdaTimestampResolverFactory.java | 49 - .../logging/internal/LoggingConstants.java | 14 +- .../logging/internal/LoggingManager.java | 48 + ...ields.java => PowertoolsLoggedFields.java} | 32 +- .../logging/internal/PowertoolsResolver.java | 66 - .../src/main/resources/LambdaEcsLayout.json | 52 - .../src/main/resources/LambdaJsonLayout.json | 89 -- .../resources/log4j2.component.properties | 2 - .../core/layout/LambdaJsonLayoutTest.java | 173 --- .../java/org/slf4j/test/OutputChoice.java | 77 ++ .../test/java/org/slf4j/test/TestLogger.java | 452 +++++++ .../slf4j/test/TestLoggerConfiguration.java | 204 +++ .../org/slf4j/test/TestLoggerFactory.java | 78 ++ .../org/slf4j/test/TestServiceProvider.java | 76 ++ .../powertools/logging/LoggingUtilsTest.java | 70 +- ...erToolLogEventEnabledWithCustomMapper.java | 63 - .../PowertoolsLogAlbCorrelationId.java | 8 +- ...olsLogApiGatewayHttpApiCorrelationId.java} | 10 +- ...olsLogApiGatewayRestApiCorrelationId.java} | 10 +- .../PowertoolsLogAppSyncCorrelationId.java | 37 + ...tate.java => PowertoolsLogClearState.java} | 17 +- ...sabled.java => PowertoolsLogDisabled.java} | 2 +- ...va => PowertoolsLogDisabledForStream.java} | 2 +- .../handlers/PowertoolsLogEnabled.java | 52 + ...ava => PowertoolsLogEnabledForStream.java} | 2 +- .../logging/handlers/PowertoolsLogError.java | 28 + ...ntEnabled.java => PowertoolsLogEvent.java} | 4 +- ...PowertoolsLogEventBridgeCorrelationId.java | 8 +- ...d.java => PowertoolsLogEventDisabled.java} | 4 +- ....java => PowertoolsLogEventForStream.java} | 8 +- .../handlers/PowertoolsLogResponse.java | 28 + .../PowertoolsLogResponseForStream.java | 35 + ...ava => PowertoolsLogSamplingDisabled.java} | 19 +- ...java => PowertoolsLogSamplingEnabled.java} | 12 +- .../internal/LambdaLoggingAspectTest.java | 709 ++++++++--- .../logging/internal/TestLoggingManager.java | 32 + .../org.slf4j.spi.SLF4JServiceProvider | 1 + ...powertools.logging.internal.LoggingManager | 15 + .../src/test/resources/log4j2.xml | 16 - .../test/resources/s3EventNotification.json | 38 - .../src/test/resources/testlogger.properties | 3 + powertools-metrics/pom.xml | 7 +- powertools-parameters/pom.xml | 7 +- powertools-serialization/pom.xml | 6 +- .../powertools/utilities/JsonConfig.java | 2 +- powertools-tracing/pom.xml | 7 +- powertools-validation/pom.xml | 2 +- .../validation/ValidationConfig.java | 2 +- spotbugs-exclude.xml | 45 +- 147 files changed, 6070 insertions(+), 2778 deletions(-) delete mode 100644 examples/powertools-examples-core-utilities/kotlin/src/test/kotlin/helloworld/AppTest.kt create mode 100644 powertools-logging/powertools-logging-log4j/pom.xml create mode 100644 powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java rename powertools-logging/{src/main/java/software/amazon/lambda/powertools/logging/internal => powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver}/PowertoolsResolverFactory.java (71%) create mode 100644 powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/Log4jLoggingManager.java create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowerToolsResolverFactoryTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/resources/junit-platform.properties create mode 100644 powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml create mode 100644 powertools-logging/powertools-logging-logback/pom.xml create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaEcsSerializer.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackLoggingManager.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/LogbackLoggingManagerTest.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEnabled.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/resources/junit-platform.properties create mode 100644 powertools-logging/powertools-logging-logback/src/test/resources/logback-test.xml create mode 100644 powertools-logging/spotbugs-exclude.xml rename powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/{CorrelationIdPathConstants.java => CorrelationIdPaths.java} (69%) delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefautlLoggingManager.java delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManager.java rename powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/{DefaultLambdaFields.java => PowertoolsLoggedFields.java} (59%) delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java delete mode 100644 powertools-logging/src/main/resources/LambdaEcsLayout.json delete mode 100644 powertools-logging/src/main/resources/LambdaJsonLayout.json delete mode 100644 powertools-logging/src/main/resources/log4j2.component.properties delete mode 100644 powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java create mode 100644 powertools-logging/src/test/java/org/slf4j/test/OutputChoice.java create mode 100644 powertools-logging/src/test/java/org/slf4j/test/TestLogger.java create mode 100644 powertools-logging/src/test/java/org/slf4j/test/TestLoggerConfiguration.java create mode 100644 powertools-logging/src/test/java/org/slf4j/test/TestLoggerFactory.java create mode 100644 powertools-logging/src/test/java/org/slf4j/test/TestServiceProvider.java delete mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolApiGatewayHttpApiCorrelationId.java => PowertoolsLogApiGatewayHttpApiCorrelationId.java} (78%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolApiGatewayRestApiCorrelationId.java => PowertoolsLogApiGatewayRestApiCorrelationId.java} (78%) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAppSyncCorrelationId.java rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowertoolsLogEnabledWithClearState.java => PowertoolsLogClearState.java} (67%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerToolDisabled.java => PowertoolsLogDisabled.java} (91%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerToolDisabledForStream.java => PowertoolsLogDisabledForStream.java} (92%) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabled.java rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolEnabledForStream.java => PowertoolsLogEnabledForStream.java} (93%) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogError.java rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerToolLogEventEnabled.java => PowertoolsLogEvent.java} (92%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerToolLogEventDisabled.java => PowertoolsLogEventDisabled.java} (89%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerToolLogEventEnabledForStream.java => PowertoolsLogEventForStream.java} (80%) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponse.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogResponseForStream.java rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolEnabled.java => PowertoolsLogSamplingDisabled.java} (68%) rename powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/{PowerLogToolSamplingEnabled.java => PowertoolsLogSamplingEnabled.java} (74%) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/TestLoggingManager.java create mode 100644 powertools-logging/src/test/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider create mode 100644 powertools-logging/src/test/resources/META-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager delete mode 100644 powertools-logging/src/test/resources/log4j2.xml delete mode 100644 powertools-logging/src/test/resources/s3EventNotification.json create mode 100644 powertools-logging/src/test/resources/testlogger.properties diff --git a/docs/core/logging.md b/docs/core/logging.md index 70781b1b2..f0fba760a 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -5,24 +5,34 @@ 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 +* Allows additional keys to be appended to the structured log at any point in time -## 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. In both cases you need to configure _aspectj_ +to weave the code and make sure the annotation is processed. + +#### Maven +=== "log4j2" + + ```xml hl_lines="3-7 24-27" <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>{{ powertools.version }}</version> </dependency> ... @@ -60,14 +70,14 @@ 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-7 24-27" <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-logback</artifactId> <version>{{ powertools.version }}</version> </dependency> ... @@ -78,13 +88,13 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14.0</version> + <version>1.13.1</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> @@ -105,7 +115,9 @@ 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" plugins { @@ -118,19 +130,19 @@ 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-log4j:{{ powertools.version }}' } sourceCompatibility = 11 targetCompatibility = 11 ``` -=== "Gradle Java 1.8" +=== "logback" ```groovy hl_lines="3 11" 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' } repositories { @@ -138,27 +150,61 @@ 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-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_LOG_EVENT` | boolean | Specify if the incoming Lambda event should be logged. See [Logging event](#logging-incoming-event) | +| `POWERTOOLS_LOG_RESPONSE` | boolean | Specify if the Lambda response should be logged. See [logging response](#logging-handler-response) | +| `POWERTOOLS_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/tree/v2/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 +214,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,152 +224,225 @@ 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. -When debugging in non-production environments, you can instruct Logger to log the incoming event with `@Logger(logEvent = true)` or via `POWERTOOLS_LOGGER_LOG_EVENT=true` environment variable. +When enabled, you should keep Powertools 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 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 + +To use Lambda Powertools for AWS Lambda Logging, use the `@Logging` annotation in your code and the standard _SLF4J_ logger: + +=== "PaymentFunction.java" + + ```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> { +## Standard structured keys + +Your logs will always include the following keys in your structured logging: + +| 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"));`) | + +### Log messages as JSON +By default, `message` is logged as a `String` (e.g `"message": "The message"`). When logging JSON content, +you may want to avoid the escaped String (`"message:"{\"key\":\"value\"}"`) for better readability. +You can use `LoggingUtils.logMessagesAsJson(true)` to enable this programmatically. + +=== "PaymentFunction.java" + + ```java hl_lines="14 15 17-20" + import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + import software.amazon.lambda.powertools.logging.LoggingUtils; + import software.amazon.lambda.powertools.utilities.JsonConfig; + // ... 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) + @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... + Order order = extractDataFrom(input).as(Order.class); + + // logged as a String + LOGGER.debug("{}", JsonConfig.get().getObjectMapper().writeValueAsString(order)); + + // Logged as JSON + LoggingUtils.logMessagesAsJson(true); + LOGGER.debug("{}", JsonConfig.get().getObjectMapper().writeValueAsString(order)); + LoggingUtils.logMessagesAsJson(false); + + // ... } } ``` -### Customising fields in logs +=== "Order.java" -- 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: + ```java + public class Order { + private String id; + private Date date; + private Double amount; + } + ``` -=== "log4j2.component.properties" +=== "Example CloudWatch Logs" - ```properties hl_lines="1 2" - log4j.layout.jsonTemplate.timestampFormatPattern=yyyy-MM-dd'T'HH:mm:ss.SSSZz - log4j.layout.jsonTemplate.timeZone=Europe/Oslo + ```json hl_lines="3 9-13" + { + "level": "DEBUG", + "message": "{\"id\":\"435iuh2j3hb4\", \"date\":\"2023-12-01T14:48:59\", \"amount\":435.5}", + "timestamp": "2023-12-01T14:49:19.293Z", + "service": "payment", + } + { + "level": "DEBUG", + "message": { + "id": "435iuh2j3hb4", + "date": "2023-12-01T14:48:59", + "amount":435.5 + }, + "timestamp": "2023-12-01T14:49:19.312Z", + "service": "payment", + } ``` -- 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). +You can also achieve this more broadly for all JSON messages (see advanced configuration for [log4j](#log-messages-as-json_1) & [logback](#log-messages-as-json_2)). - Use `LambdaEcsLayout.json` as `eventTemplateUri` when configuring `JsonTemplateLayout`. +## Additional structured keys -=== "log4j2.xml" +### Logging Lambda context information +The following keys will also be added to all your structured logs (unless [configured otherwise](#more-customization_1)): - ```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> - ``` +| 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 | -## Setting a Correlation ID +### Logging additional keys -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"}. +#### Logging a correlation ID -=== "App.java" +You can set a correlation ID using the `correlationIdPath` attribute of the `@Logging`annotation, +by passing a [JMESPath expression](https://jmespath.org/tutorial.html){target="_blank"}, +including our custom [JMESPath Functions](../utilities/serialization.md#built-in-functions). - ```java hl_lines="8" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +=== "AppCorrelationIdPath.java" + + ```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" +=== "Example HTTP Event" ```json hl_lines="3" { @@ -333,42 +452,89 @@ 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", - "function_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. -=== "App.java" +**setCorrelationId method** - ```java hl_lines="10" - import software.amazon.lambda.powertools.logging.CorrelationIdPathConstants; +You can also use `LoggingUtils.setCorrelationId()` method to inject it anywhere else in your code. - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +=== "AppSetCorrelationId.java" + + ```java hl_lines="8" + public class AppSetCorrelationId implements RequestHandler<ScheduledEvent, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppSetCorrelationId.class); + + @Logging + public String handleRequest(final ScheduledEvent event, final Context context) { + // ... + LoggingUtils.setCorrelationId(event.getId()); + LOGGER.info("Scheduled Event") + // ... + } + } + ``` + +=== "Example Schedule Event" + + ```json hl_lines="2" + { + "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", + "detail-type": "Scheduled Event", + "source": "aws.events", + "account": "123456789012", + "time": "2023-12-01T14:49:19Z", + "region": "us-east-1", + "resources": [ + "arn:aws:events:us-east-1:123456789012:rule/ExampleRule" + ], + "detail": {} + } + ``` + +=== "CloudWatch Logs with correlation id" + + ```json hl_lines="6" + { + "level": "INFO", + "message": "Scheduled Event", + "timestamp": "2023-12-01T14:49:19.293Z", + "service": "payment", + "correlation_id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c" + } + ``` +???+ tip + You can retrieve correlation IDs via `LoggingUtils.getCorrelationId()` method if needed. + +**Known correlation IDs** + +To ease routine tasks like extracting correlation ID from popular event sources, +we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions). + +=== "AppCorrelationId.java" + + ```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") + // ... } } ``` @@ -377,316 +543,581 @@ 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", - "function_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`. +???+ warning "Custom keys 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). -=== "App.java" +To append an additional key in your logs, you can use the `LoggingUtils.appendKey()` or `LoggingUtils.appendKeys()` for multiple keys: - ```java hl_lines="11 19" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +=== "PaymentFunction.java" + + ```java hl_lines="8 9 15 16" + 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"); - ... - - ... - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); - customKeys.put("test1", "value1"); + // ... + LoggingUtils.appendKey("orderId", order.getId()); + LOGGER.info("Collecting payment"); - LoggingUtils.appendKeys(customKeys); - ... + // ... + Map<String, String> customKeys = new HashMap<>(); + customKeys.put("paymentId", payment.getId()); + customKeys.put("amount", payment.getAmount); + LoggingUtils.appendKeys(customKeys); + LOGGER.info("Payment successful"); } } ``` +=== "Example CloudWatch Logs" + + ```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 + } + ``` + +???+ tip "Additional keys are based on the MDC" + 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"}). + + `LoggingUtils.appendKey("key", "value")` is equivalent to `MDC.put("key", "value")`. + ### Removing additional keys -You can remove any additional key from entry using `LoggingUtils.removeKeys()`. +You can remove any additional key from entry using `LoggingUtils.removeKey()` or `LoggingUtils.removeKeys()` for multiple keys: -=== "App.java" +=== "PaymentFunction.java" ```java hl_lines="19 20" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + 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(logResponse = true) 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.appendKey("orderId", order.getId()); + LOGGER.info("Collecting payment"); + // ... + Map<String, String> customKeys = new HashMap<>(); + customKeys.put("paymentId", payment.getId()); + customKeys.put("amount", payment.getAmount); LoggingUtils.appendKeys(customKeys); - ... - LoggingUtils.removeKey("test"); - LoggingUtils.removeKeys("test1", "test2"); - ... + LOGGER.info("Payment successful"); + + // ... + LoggingUtils.removeKey("orderId"); + LoggingUtils.removeKeys("paymentId", "amount"); + + return response; } } ``` -### Clearing all state +=== "Example CloudWatch Logs" + Response is logged (`logResponse=true`) without the additional keys: + + ```json + ... + { + "level": "INFO", + "message": { + "statusCode": 200, + "isBase64Encoded": false, + "body": ..., + "headers": ..., + "multiValueHeaders": ... + }, + "service": "payment", + "timestamp": "2023-12-01T14:49:20.118Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5" + } + ``` + +???+ tip "Additional keys are based on the MDC" + `LoggingUtils.removeKey("key")` is equivalent to `MDC.remove("key")`. -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. +#### 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 can be persisted across invocations. If you want all custom keys to be deleted, you can use +`clearState=true` attribute on the `@Logging` annotation. -=== "App.java" +=== "CreditCardFunction.java" - ```java hl_lines="8 12" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + ```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"); - ... + // ... + LoggingUtils.appendKey("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", - "function_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", - "function_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 +???+ tip "Additional keys are based on the MDC" + `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. -=== "App.java" +## Logging incoming event - ```java hl_lines="9 10" - /** - * Handler for requests to Lambda function. - */ - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { +When debugging in non-production environments, you can instruct the `@Logging` annotation to log the incoming event with `logEvent` param or via `POWERTOOLS_LOGGER_LOG_EVENT` env var. + +???+ warning + This is disabled by default to prevent sensitive info being logged + +=== "AppLogEvent.java" + + ```java hl_lines="5" + public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogEvent.class); + + @Logging(logEvent = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + } + } + ``` - static { - ObjectMapper objectMapper = new ObjectMapper(); - LoggingUtils.defaultObjectMapper(objectMapper); +???+ note + If you use this on a RequestStreamHandler, Powertools must duplicate input streams in order to log them. + +## Logging handler response + +When debugging in non-production environments, you can instruct the `@Logging` annotation to log the response with `logResponse` param or via `POWERTOOLS_LOGGER_LOG_RESPONSE` env var. + +???+ warning + This is disabled by default to prevent sensitive info being logged + +=== "AppLogResponse.java" + + ```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) { + // ... } + } + ``` + +???+ note + If you use this on a RequestStreamHandler, Powertools must duplicate output streams in order to log them. + +## 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. You can instruct the `@Logging` annotation to log this kind of exception +with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. + +???+ warning + This is disabled by default to prevent double logging + +=== "AppLogResponse.java" + + ```java hl_lines="5" + public class AppLogError implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - @Logging(logEvent = true) + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogError.class); + + @Logging(logError = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... + // ... } } ``` +# Advanced + ## Sampling debug logs -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. +You can dynamically set a percentage of your logs to`DEBUG` level to be included in the logger output, regardless of configured log leve, using the`POWERTOOLS_LOGGER_SAMPLE_RATE` environment variable or +via `samplingRate` attribute on the `@Logging` annotation. !!! info Configuration on environment variable is given precedence over sampling rate configuration on annotation, provided it's in valid value range. === "Sampling via annotation attribute" - ```java hl_lines="8" - /** - * Handler for requests to Lambda function. - */ + ```java hl_lines="5" public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - Logger log = LogManager.getLogger(App.class); + 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"); } } ``` === "Sampling via environment variable" - ```yaml hl_lines="9" + ```yaml hl_lines="8" Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - Environment: - Variables: - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 + PaymentFunction: + Type: AWS::Serverless::Function + Properties: + ... + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 + ``` -## AWS Lambda Advanced Logging Controls (ALC) +## Built-in Correlation ID expressions -!!!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. +You can use any of the following built-in JMESPath expressions as part of `@Logging(correlationIdPath = ...)`: -<!-- 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. +???+ note "Note: Any object key named with `-` must be escaped" + For example, **`request.headers."x-amzn-trace-id"`**. -When enabled, you should keep `Logger` and ALC log level in sync to avoid data loss. +| 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 | -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 +## Customising fields in logs - Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN" - Note over Application Logger: POWERTOOLS_LOG_LEVEL="DEBUG" +Powertools for AWS Lambda comes with default json structure ([standard fields](#standard-structured-keys) & [lambda context fields](#logging-lambda-context-information)). - 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 +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> ``` -### Priority of log level settings in Powertools for AWS Lambda +The `JsonTemplateLayout` is automatically configured with the provided template: -We prioritise log level settings in this order: +??? example "LambdaJsonLayout.json" + ```json + { + "level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "powertools", + "field": "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" + }, + "": { + "$resolver": "powertools" + } + } + ``` -1. `AWS_LAMBDA_LOG_LEVEL` environment variable -2. `POWERTOOLS_LOG_LEVEL` environment variable +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: -If you set `Logger` level lower than ALC, we will emit a warning informing you that your messages will be discarded by Lambda. +#### Log messages as JSON +`message` field is not handled with the standard [`MessageResolver`](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-template-resolver-message){target="_blank"} but by the `PowertoolsResolver`. +With this resolver, you can choose to log all the JSON messages as JSON and not as String. -> **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. +=== "my-custom-template.json" -### Timestamp format + ```json + { + "message": { + "$resolver": "powertools", + "field": "message", + "asJson": true + } + } + ``` -When the Advanced Logging Controls feature is enabled, Powertools for AWS Lambda must comply with the timestamp format required by AWS Lambda, which is [RFC3339](https://www.rfc-editor.org/rfc/rfc3339). -In this case the format will be `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`. +#### Customising date format -## Upgrade to JsonTemplateLayout from deprecated LambdaJsonLayout configuration in log4j2.xml +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: -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. +=== "my-custom-template.json" -Below examples shows deprecated and new configuration of `log4j2.xml`. + ```json + { + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd HH:mm:ss", + "timeZone": "Europe/Paris", + } + }, + } + ``` -=== "Deprecated configuration of log4j2.xml" +=== "log4j2.component.properties" - ```xml hl_lines="5" - <?xml version="1.0" encoding="UTF-8"?> - <Configuration> - <Appenders> - <Console name="JsonAppender" target="SYSTEM_OUT"> - <LambdaJsonLayout compact="true" eventEol="true"/> - </Console> - </Appenders> - <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> - </Logger> - <Root level="info"> - <AppenderRef ref="JsonAppender"/> - </Root> - </Loggers> - </Configuration> + ```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 Powertools [`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: + +#### Log messages as JSON + +With the following configuration, you choose to log all the JSON messages as JSON and not as String (default is `false`): + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <logMessagesAsJson>true</logMessagesAsJson> + </encoder> +``` + +#### 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> +``` + +## Override default object mapper + +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. + +=== "App.java" + + ```java hl_lines="6-10" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + static { + ObjectMapper objectMapper = new ObjectMapper() + .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + LoggingUtils.setObjectMapper(objectMapper); + } + + @Logging(logEvent = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + } + } ``` -=== "New configuration of log4j2.xml" +## 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:LambdaJsonLayout.json" /> + <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> @@ -694,3 +1125,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> + ``` \ No newline at end of file diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index d135d7210..dc08ef51e 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,5 +1,5 @@ .md-grid { - max-width: 81vw + max-width: 90vw } .highlight .hll { diff --git a/examples/pom.xml b/examples/pom.xml index 3526be2a4..943cad950 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -23,7 +23,7 @@ <version>2.0.0-SNAPSHOT</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> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 36660bc96..a1b4c0bbc 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -8,13 +8,12 @@ <version>2.0.0-SNAPSHOT</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>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> <sdk.version>2.21.1</sdk.version> </properties> @@ -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> 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..e3c27c093 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() { 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 b188df501..f5d7102b5 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(KinesisBatchHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(KinesisBatchHandler.class); private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler; public KinesisBatchHandler() { 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/SqsBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandler.java index bb9d704d3..27689485c 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,14 @@ 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.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 SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { - private final static Logger LOGGER = LogManager.getLogger(SqsBatchHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SqsBatchHandler.class); private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; public SqsBatchHandler() { 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..4050ab98b 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,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.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.services.sqs.SqsClient; @@ -27,7 +27,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; diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 864ea5fe6..bee97e52a 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -7,16 +7,15 @@ <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (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>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> <aws.sdk.version>2.21.0</aws.sdk.version> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencyManagement> @@ -49,19 +48,9 @@ </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> - </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> @@ -91,14 +80,6 @@ </exclusion> </exclusions> </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-jcl</artifactId> - <version>${log4j.version}</version> - </dependency> - - - </dependencies> <build> 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-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 822e87633..f8d340f3b 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -2,7 +2,7 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with CDK</name> + <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 --> @@ -14,7 +14,7 @@ <log4j.version>2.20.0</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencies> diff --git a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java index 988da2a73..18eea0560 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java @@ -29,8 +29,8 @@ 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.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; @@ -44,8 +44,7 @@ * 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); @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 03971c406..38cf96c1c 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -28,8 +28,9 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' implementation 'com.amazonaws:aws-lambda-java-events:3.11.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.0.0-SNAPSHOT' - aspect 'software.amazon.lambda:powertools-logging:2.0.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.0.0-SNAPSHOT' aspect 'software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT' } 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 index fccc63b9a..36ef72ae7 100644 --- 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 @@ -29,8 +29,8 @@ 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.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; @@ -44,7 +44,7 @@ * 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); @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 1c16db0db..b37ef6d31 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -9,16 +9,16 @@ repositories { } 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.11.0") - implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2") + 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.11.3") + 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.0.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-logging:2.0.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0-SNAPSHOT") aspect("software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT") - testImplementation("junit:junit:4.13.2") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10") } kotlin { 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 index ed4cf267a..1c925d4f4 100644 --- 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 @@ -18,7 +18,8 @@ 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.apache.logging.log4j.LogManager +import org.slf4j.Logger +import org.slf4j.LoggerFactory import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger import software.amazon.cloudwatchlogs.emf.model.DimensionSet import software.amazon.cloudwatchlogs.emf.model.Unit @@ -92,5 +93,5 @@ class App : RequestHandler<APIGatewayProxyRequestEvent?, APIGatewayProxyResponse } } - private val log = LogManager.getLogger(App::class) + private val log = LoggerFactory.getLogger(this::class.java) } diff --git a/examples/powertools-examples-core-utilities/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core-utilities/kotlin/src/test/kotlin/helloworld/AppTest.kt deleted file mode 100644 index 8aae081e8..000000000 --- a/examples/powertools-examples-core-utilities/kotlin/src/test/kotlin/helloworld/AppTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package helloworld - - -import org.junit.Assert -import org.junit.Test - -class AppTest { - @Test - fun successfulResponse() { - val app = App() - val result = app.handleRequest(null, null) - Assert.assertEquals(200, result.statusCode.toLong()) - Assert.assertEquals("application/json", result.headers["Content-Type"]) - val content = result.body - Assert.assertNotNull(content) - Assert.assertTrue(""""message"""" in content) - Assert.assertTrue(""""hello world"""" in content) - Assert.assertTrue(""""location"""" in content) - } -} diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 3ddecc5e4..c9d90d281 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -2,17 +2,16 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with SAM</name> + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> <properties> - <log4j.version>2.20.0</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencies> @@ -23,7 +22,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -41,16 +40,6 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.3</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> 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 index fccc63b9a..36ef72ae7 100644 --- 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 @@ -29,8 +29,8 @@ 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.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; @@ -44,7 +44,7 @@ * 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); @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 7ebfdfc00..32cca9bb4 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -2,17 +2,16 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> <properties> - <log4j.version>2.20.0</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencies> @@ -23,7 +22,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -41,16 +40,6 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.3</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> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index f508d9d3d..c6f838619 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -2,17 +2,16 @@ 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) library Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> <properties> - <log4j.version>2.20.0</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencies> @@ -23,7 +22,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -41,16 +40,6 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.3</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> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index b7fd3d832..5a040fec0 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -20,13 +20,12 @@ <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> + <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> <properties> - <log4j.version>2.20.0</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <dependencies> @@ -37,7 +36,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -55,16 +54,6 @@ <artifactId>aws-lambda-java-events</artifactId> <version>3.11.3</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> @@ -83,11 +72,6 @@ <version>5.9.3</version> <scope>test</scope> </dependency> - <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-tests</artifactId> - <version>1.1.1</version> - </dependency> </dependencies> <build> diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index 72fa621ad..cf0c0ee31 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -26,8 +26,8 @@ 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; @@ -37,7 +37,7 @@ 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); diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 8d7fbd4f4..3630811f3 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -5,18 +5,18 @@ <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-parameters</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Parameters</name> + <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</aspectj.version> + <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> diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java index 9a1d3636b..9c3c422cf 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java @@ -29,15 +29,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 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); // Annotation-style injection from secrets manager @SecretsParam(key = "/powertools-java/userpwd") diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 468c7d161..2c8fc951a 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -5,7 +5,7 @@ <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Examples - Serialization</name> + <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> <properties> <maven.compiler.source>11</maven.compiler.source> @@ -15,7 +15,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${project.version}</version> </dependency> <dependency> diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java b/examples/powertools-examples-serialization/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/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/SQSEventDeserializationFunction.java b/examples/powertools-examples-serialization/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/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-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 2d4ef07a0..ed33568cb 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -19,18 +19,18 @@ <version>2.0.0-SNAPSHOT</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> - <aspectj.version>1.9.20</aspectj.version> + <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> diff --git a/pom.xml b/pom.xml index 1d35a990f..b7227cf8a 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ <version>2.0.0-SNAPSHOT</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> @@ -44,6 +44,8 @@ <module>powertools-common</module> <module>powertools-serialization</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-metrics</module> <module>powertools-parameters</module> @@ -76,7 +78,8 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.0</log4j.version> + <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.15.3</jackson.version> <aws.sdk.version>2.21.0</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> @@ -96,7 +99,8 @@ <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> + <jmespath.version>0.6.0</jmespath.version> + <elastic.version>1.5.0</elastic.version> </properties> <distributionManagement> @@ -123,6 +127,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> @@ -200,6 +214,11 @@ <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + <version>${log4j.version}</version> + </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> @@ -220,6 +239,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-core</artifactId> @@ -519,7 +548,6 @@ </plugins> </build> </profile> - <profile> <id>jdk16</id> <activation> @@ -605,6 +633,34 @@ </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> + <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.3</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> </profile> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index d7246b816..eaafdb56e 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -10,7 +10,7 @@ </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> + <name>Powertools for AWS Lambda (Java) - Batch messages</name> <build> <plugins> 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..83a8bf7dd 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 @@ -30,7 +30,7 @@ * @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; 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..ad1dd302d 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 @@ -34,7 +34,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; 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..b634f9b62 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 @@ -31,11 +31,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; diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 0e6763e25..79071d532 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -27,7 +27,7 @@ <version>2.0.0-SNAPSHOT</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. diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 82e2c1d17..363d2e944 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -27,7 +27,7 @@ <version>2.0.0-SNAPSHOT</version> </parent> - <name>Powertools for AWS Lambda (Java) library Common Internal Utilities</name> + <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> <url>https://aws.amazon.com/lambda/</url> <issueManagement> @@ -55,17 +55,17 @@ <dependencies> <dependency> - <groupId>com.amazonaws</groupId> - <artifactId>aws-lambda-java-core</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> </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> - <version>${log4j.version}</version> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-core</artifactId> </dependency> <!-- Test dependencies --> diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java index bdcbdc010..d27ac1aa2 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java @@ -17,12 +17,6 @@ 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_SAM_LOCAL = "AWS_SAM_LOCAL"; diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java index a2830e467..bfacd5204 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java @@ -69,7 +69,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)) { diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java index 585c38c59..8ae10ad62 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java @@ -37,8 +37,8 @@ public class UserAgentConfigurator { public static final String AWS_EXECUTION_ENV = "AWS_EXECUTION_ENV"; 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() { @@ -99,7 +99,7 @@ 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; diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index 589aab703..4ad170600 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -31,9 +31,6 @@ import org.aspectj.lang.Signature; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.common.internal.LambdaConstants; -import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.common.internal.SystemWrapper; class LambdaHandlerProcessorTest { diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 8740dcb0b..a36d464ea 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -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,10 +29,6 @@ <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> 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..bfde65bc8 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,6 @@ package software.amazon.lambda.powertools.e2e; -import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; @@ -24,41 +23,26 @@ 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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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; @@ -139,7 +123,7 @@ public Object createResult(String input, Context context) { SQSEvent event = serializer.fromJson(input); if (event.getRecords().get(0).getEventSource().equals("aws:sqs")) { LOGGER.info("Running for SQS"); - LOGGER.info(event); + LOGGER.info(event.toString()); return sqsHandler.processBatch(event, context); } diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index a0e6bd4fc..da2bbfb80 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -19,11 +19,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 277e76fc1..0728404bf 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -23,16 +23,12 @@ </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> - </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index d887341c5..8cb2cb52c 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -23,16 +23,12 @@ </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> - </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 222c5ab2e..88feda09b 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -15,7 +15,12 @@ <dependencies> <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-layout-template-json</artifactId> + <version>2.20.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> @@ -25,10 +30,6 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> </dependencies> <build> diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 62ebabc6e..c2634533d 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -16,18 +16,16 @@ 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; import software.amazon.lambda.powertools.logging.LoggingUtils; 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()); LOG.info(input.getMessage()); diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 328d30485..2d6a9a06a 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -15,7 +15,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 2c73de977..412593da9 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -22,8 +22,7 @@ <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> - <aspectj.version>1.9.20</aspectj.version> + <aspectj.version>1.9.20.1</aspectj.version> </properties> <modules> @@ -33,7 +32,10 @@ <module>logging</module> <module>tracing</module> <module>metrics</module> + <module>batch</module> <module>idempotency</module> + <module>largemessage</module> + <module>largemessage_idempotent</module> <module>parameters</module> <module>validation-alb-event</module> <module>validation-apigw-event</module> @@ -55,7 +57,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>${lambda.powertools.version}</version> </dependency> <dependency> @@ -108,11 +110,6 @@ <artifactId>aws-lambda-java-serialization</artifactId> <version>${lambda.java.serialization}</version> </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j2-impl</artifactId> - <version>${log4j.version}</version> - </dependency> </dependencies> </dependencyManagement> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1f5bd6347..a64e7a354 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -24,7 +24,7 @@ </parent> <artifactId>powertools-e2e-tests</artifactId> - <name>Powertools for AWS Lambda (Java)library End-to-end tests</name> + <name>Powertools for AWS Lambda (Java) - End-to-end tests</name> <description>Powertools for AWS Lambda (Java)End-To-End Tests</description> <properties> @@ -184,6 +184,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> 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 b060879d3..f78500c65 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 @@ -83,7 +83,7 @@ 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()); @@ -91,6 +91,6 @@ public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { 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-idempotency/pom.xml b/powertools-idempotency/pom.xml index f7f44dc99..eda7bd982 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -27,7 +27,7 @@ <artifactId>powertools-idempotency</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Idempotency</name> + <name>Powertools for AWS Lambda (Java) - Idempotency</name> <description> </description> @@ -55,6 +55,11 @@ </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> @@ -86,10 +91,6 @@ <artifactId>url-connection-client</artifactId> <version>${aws.sdk.version}</version> </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - </dependency> <!-- Test dependencies --> <dependency> 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 index 6da826c45..bd564caf8 100644 --- 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 @@ -82,7 +82,7 @@ private void setPersistenceStore(BasePersistenceStore persistenceStore) { } private static class Holder { - private final static Idempotency instance = new Idempotency(); + private static final Idempotency instance = new Idempotency(); } public static class Config { diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java index 76c36ae9f..43e191fc2 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java @@ -25,8 +25,8 @@ 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; @@ -35,7 +35,7 @@ import software.amazon.lambda.powertools.utilities.JsonConfig; public class IdempotencyFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); + private static final Logger LOG = LoggerFactory.getLogger(IdempotencyFunction.class); public boolean handlerExecuted = false; diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index a56623518..af031ff21 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -29,7 +29,7 @@ <artifactId>powertools-large-messages</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Large messages</name> + <name>Powertools for AWS Lambda (Java) - Large messages</name> <issueManagement> <system>GitHub Issues</system> @@ -54,6 +54,11 @@ </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 21a221967..56ca0e62b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -17,48 +17,26 @@ 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>2.0.0-SNAPSHOT</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> + <artifactId>powertools-logging</artifactId> + <packaging>jar</packaging> + <name>Powertools for AWS Lambda (Java) - Logging</name> + <description>Set of utility for better logging - common</description> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-serialization</artifactId> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> @@ -68,20 +46,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> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</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.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> </dependency> <!-- Test dependencies --> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml new file mode 100644 index 000000000..df6154560 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -0,0 +1,137 @@ +<?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.0.0-SNAPSHOT</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> + + <!-- 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.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> + </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> + <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-checkstyle-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <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> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file 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..95086a085 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java @@ -0,0 +1,286 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.lang.Boolean.TRUE; +import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; +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 com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +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.message.Message; +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.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 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]); + } + }; + + /** + * Use a custom message resolver to permit to log json string in json format without escaped quotes. + */ + private static final class MessageResolver implements EventResolver { + private final ObjectMapper mapper = new ObjectMapper() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + private final boolean logMessagesAsJsonGlobal; + + public MessageResolver(boolean logMessagesAsJson) { + this.logMessagesAsJsonGlobal = logMessagesAsJson; + } + + public boolean isValidJson(String json) { + if (!(json.startsWith("{") || json.startsWith("["))) { + return false; + } + try { + mapper.readTree(json); + } catch (JacksonException e) { + return false; + } + return true; + } + + @Override + public boolean isResolvable(LogEvent logEvent) { + final Message msg = logEvent.getMessage(); + return null != msg && null != msg.getFormattedMessage(); + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + String message = logEvent.getMessage().getFormattedMessage(); + + String logMessagesAsJsonLocal = logEvent.getContextData().getValue(LOG_MESSAGES_AS_JSON); + Boolean logMessagesAsJson = null; + if (logMessagesAsJsonLocal != null) { + logMessagesAsJson = Boolean.parseBoolean(logMessagesAsJsonLocal); + } + + if (((logMessagesAsJsonGlobal && logMessagesAsJson == null) || TRUE.equals(logMessagesAsJson)) + && isValidJson(message)) { + jsonWriter.writeRawString(message); + } else { + jsonWriter.writeString(message); + } + } + } + + private static final EventResolver NON_POWERTOOLS_FIELD_RESOLVER = + (LogEvent logEvent, JsonWriter jsonWriter) -> { + StringBuilder stringBuilder = jsonWriter.getStringBuilder(); + // remove dummy field to kick in powertools resolver + stringBuilder.setLength(stringBuilder.length() - 4); + + // Inject all the context information. + ReadOnlyStringMap contextData = logEvent.getContextData(); + contextData.forEach((key, value) -> { + if (!PowertoolsLoggedFields.stringValues().contains(key) && !LOG_MESSAGES_AS_JSON.equals(key)) { + jsonWriter.writeSeparator(); + jsonWriter.writeString(key); + stringBuilder.append(':'); + jsonWriter.writeValue(value); + } + }); + }; + + private final EventResolver internalResolver; + + private static final Map<String, EventResolver> eventResolverMap = 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 }, + { 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 { + boolean logMessagesAsJson = false; + if (config.exists("asJson")) { + logMessagesAsJson = config.getBoolean("asJson"); + } + if ("message".equals(fieldName)) { + internalResolver = new MessageResolver(logMessagesAsJson); + } 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/log4/internal/Log4jLoggingManager.java b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/Log4jLoggingManager.java new file mode 100644 index 000000000..4e57a8e45 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/Log4jLoggingManager.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.log4.internal; + +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.LoggingManager; + +/** + * LoggingManager for Log4j2 (see {@link LoggingManager}). + */ +public class Log4jLoggingManager implements LoggingManager { + + /** + * @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()); + } +} 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..19f13f199 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json @@ -0,0 +1,89 @@ +{ + "@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" + }, + "": { + "$resolver": "powertools" + } +} \ No newline at end of file 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..d8d8810f6 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -0,0 +1,72 @@ +{ + "level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "powertools", + "field": "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" + }, + "": { + "$resolver": "powertools" + } +} 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..d444c5525 --- /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.log4.internal.Log4jLoggingManager \ No newline at end of file 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..4cf798a47 --- /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 static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import com.amazonaws.services.lambda.runtime.Context; +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.Order; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; + +@Order(1) +class PowerToolsResolverFactoryTest { + + @Mock + private Context context; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + openMocks(this); + MDC.clear(); + writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + setupContext(); + // 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{ + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } + + @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:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"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\":\"012345678910\",\"faas.id\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"faas.name\":\"testFunction\",\"faas.version\":\"1\",\"faas.memory\":1024,\"faas.execution\":\"RequestId\",\"faas.coldstart\":true,\"trace.id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\"}\n"); + } + + private void setupContext() { + when(context.getFunctionName()).thenReturn("testFunction"); + when(context.getInvokedFunctionArn()).thenReturn( + "arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1"); + when(context.getFunctionVersion()).thenReturn("1"); + when(context.getMemoryLimitInMB()).thenReturn(1024); + when(context.getAwsRequestId()).thenReturn("RequestId"); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.java new file mode 100644 index 000000000..a00b78906 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.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 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 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 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.mockito.Mock; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsJsonMessage; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; + +@Order(2) +class PowertoolsMessageResolverTest { + + @Mock + private Context context; + + @BeforeEach + void setUp() throws IOException { + openMocks(this); + MDC.clear(); + setupContext(); + + 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 { + //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(); + } + + @Test + void shouldLogJsonMessageWithoutEscapedStringsWhenSettingLogAsJson() { + // GIVEN + PowertoolsJsonMessage requestHandler = new PowertoolsJsonMessage(); + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("eu-west-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("\"message\":{\"messageId\":\"1212abcd\",\"receiptHandle\":null,\"body\":\"plop\",\"md5OfBody\":null,\"md5OfMessageAttributes\":null,\"eventSourceArn\":null,\"eventSource\":\"eb\",\"awsRegion\":\"eu-west-1\",\"attributes\":null,\"messageAttributes\":{\"keyAttribute\":{\"stringValue\":null,\"binaryValue\":null,\"stringListValues\":[\"val1\",\"val2\",\"val3\"],\"binaryListValues\":null,\"dataType\":null}}}") + .contains("\"message\":\"1212abcd\"") + .contains("\"message\":\"{\\\"key\\\":\\\"value\\\"}\"") + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .doesNotContain(LoggingUtils.LOG_MESSAGES_AS_JSON); + } + + @Test + void shouldLogStringMessageWhenNotJson() { + // GIVEN + PowertoolsLogEnabled requestHandler = new PowertoolsLogEnabled(); + + // WHEN + requestHandler.handleRequest(null, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("\"message\":\"Test debug event\""); + } + + 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"); + } +} \ No newline at end of file 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..1aa98fdef --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverTest.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 org.apache.logging.log4j.layout.template.json.resolver; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; +import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; +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.mockito.MockedStatic; +import software.amazon.lambda.powertools.common.internal.SystemWrapper; +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 shouldResolveRegion() { + try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { + mocked.when(() -> getenv("AWS_REGION")) + .thenReturn("eu-central-2"); + + 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/Log4jLoggingManagerTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java new file mode 100644 index 000000000..69e1ee710 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java @@ -0,0 +1,51 @@ +package software.amazon.lambda.powertools.logging.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.slf4j.event.Level.DEBUG; +import static org.slf4j.event.Level.ERROR; +import static org.slf4j.event.Level.WARN; + +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; +import software.amazon.lambda.powertools.logging.log4.internal.Log4jLoggingManager; + +class Log4jLoggingManagerTest { + + private final static Logger LOG = LoggerFactory.getLogger(Log4jLoggingManagerTest.class); + private final static Logger ROOT = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + + @Test + @Order(1) + 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 + @Order(2) + 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); + } +} diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java new file mode 100644 index 000000000..3d196e5fb --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.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.logging.internal.handler; + +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +public class PowertoolsJsonMessage implements RequestHandler<SQSEvent.SQSMessage, String> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsJsonMessage.class); + + @Override + @Logging(clearState = true) + public String handleRequest(SQSEvent.SQSMessage input, Context context) { + try { + LoggingUtils.logMessagesAsJson(true); + LOG.debug(JsonConfig.get().getObjectMapper().writeValueAsString(input)); + + LoggingUtils.logMessagesAsJson(false); + LOG.debug("{\"key\":\"value\"}"); + + LOG.debug("{}", input.getMessageId()); + LOG.warn("Message body = {} and id = \"{}\"", input.getBody(), input.getMessageId()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + return input.getMessageId(); + } +} 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..faa722756 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/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 software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; + +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) { + LoggingUtils.appendKey("myKey", "myValue"); + LOG.debug("Test debug event"); + return null; + } +} 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.xml b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml new file mode 100644 index 000000000..778077bc5 --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml @@ -0,0 +1,24 @@ +<?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> + </Appenders> + <Loggers>q + <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..99fff3ab9 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -0,0 +1,137 @@ +<?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.0.0-SNAPSHOT</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> + <!-- TODO: use 1.4 if we remove JDK 1.8 support --> + <version>1.3.4</version> <!-- v1.3.x compatible with JDK 1.8, v1.4.x only compatible with JDK 11 --> + <scope>provided</scope> <!-- provided to let users change to 1.4.x when using JDK 11 --> + <exclusions> + <exclusion> + <groupId>com.sun.mail</groupId> + <artifactId>javax.mail</artifactId> + </exclusion> + </exclusions> + </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.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> + </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> + <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-checkstyle-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <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> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> + + +</project> \ No newline at end of file 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..1fc98ec67 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java @@ -0,0 +1,199 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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 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.util.Map; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.logback.internal.LambdaEcsSerializer; + + +/** + * 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 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 null; + } + + /** + * 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 + */ + @Override + public byte[] encode(ILoggingEvent event) { + final Map<String, String> mdcPropertyMap = event.getMDCPropertyMap(); + + StringBuilder builder = new StringBuilder(256); + LambdaEcsSerializer.serializeObjectStart(builder); + LambdaEcsSerializer.serializeTimestamp(builder, event.getTimeStamp(), "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "UTC"); + LambdaEcsSerializer.serializeEcsVersion(builder, ECS_VERSION); + LambdaEcsSerializer.serializeLogLevel(builder, event.getLevel()); + LambdaEcsSerializer.serializeFormattedMessage(builder, event.getFormattedMessage()); + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + if (throwableConverter != null) { + LambdaEcsSerializer.serializeException(builder, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableConverter.convert(event)); + } else if (throwableProxy instanceof ThrowableProxy) { + LambdaEcsSerializer.serializeException(builder, ((ThrowableProxy) throwableProxy).getThrowable()); + } else { + LambdaEcsSerializer.serializeException(builder, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableProxyConverter.convert(event)); + } + } + LambdaEcsSerializer.serializeServiceName(builder, LambdaHandlerProcessor.serviceName()); + LambdaEcsSerializer.serializeServiceVersion(builder, mdcPropertyMap.get(FUNCTION_VERSION.getName())); + LambdaEcsSerializer.serializeLoggerName(builder, event.getLoggerName()); + LambdaEcsSerializer.serializeThreadName(builder, event.getThreadName()); + String arn = mdcPropertyMap.get(FUNCTION_ARN.getName()); + + if (includeCloudInfo) { + LambdaEcsSerializer.serializeCloudProvider(builder, CLOUD_PROVIDER); + LambdaEcsSerializer.serializeCloudService(builder, CLOUD_SERVICE); + if (arn != null) { + String[] arnParts = arn.split(":"); + LambdaEcsSerializer.serializeCloudRegion(builder, arnParts[3]); + LambdaEcsSerializer.serializeCloudAccountId(builder, arnParts[4]); + } + } + + if (includeFaasInfo) { + LambdaEcsSerializer.serializeFunctionId(builder, arn); + LambdaEcsSerializer.serializeFunctionName(builder, mdcPropertyMap.get(FUNCTION_NAME.getName())); + LambdaEcsSerializer.serializeFunctionVersion(builder, mdcPropertyMap.get(FUNCTION_VERSION.getName())); + LambdaEcsSerializer.serializeFunctionMemory(builder, mdcPropertyMap.get(FUNCTION_MEMORY_SIZE.getName())); + LambdaEcsSerializer.serializeFunctionExecutionId(builder, + mdcPropertyMap.get(FUNCTION_REQUEST_ID.getName())); + LambdaEcsSerializer.serializeColdStart(builder, mdcPropertyMap.get(FUNCTION_COLD_START.getName())); + LambdaEcsSerializer.serializeTraceId(builder, mdcPropertyMap.get(FUNCTION_TRACE_ID.getName())); + } + LambdaEcsSerializer.serializeAdditionalFields(builder, event.getMDCPropertyMap()); + LambdaEcsSerializer.serializeObjectEnd(builder); + return builder.toString().getBytes(UTF_8); + } + + @Override + public byte[] footerBytes() { + return null; + } + + /** + * 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; + } +} 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..b951e266e --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java @@ -0,0 +1,208 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.LoggingUtils.LOG_MESSAGES_AS_JSON; + +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 software.amazon.lambda.powertools.logging.logback.internal.LambdaJsonSerializer; + +/** + * Custom encoder for logback that encodes logs in JSON format. + * It does not use a JSON library but a custom serializer ({@link LambdaJsonSerializer}) + */ +public class LambdaJsonEncoder extends EncoderBase<ILoggingEvent> { + + 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; + private boolean logMessagesAsJsonGlobal; + + @Override + public byte[] headerBytes() { + return null; + } + + @Override + public void start() { + super.start(); + throwableProxyConverter.start(); + if (throwableConverter != null) { + throwableConverter.start(); + } + } + + @Override + public byte[] encode(ILoggingEvent event) { + StringBuilder builder = new StringBuilder(256); + LambdaJsonSerializer.serializeObjectStart(builder); + LambdaJsonSerializer.serializeLogLevel(builder, event.getLevel()); + LambdaJsonSerializer.serializeFormattedMessage( + builder, + event.getFormattedMessage(), + logMessagesAsJsonGlobal, + event.getMDCPropertyMap().get(LOG_MESSAGES_AS_JSON)); + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + if (throwableConverter != null) { + LambdaJsonSerializer.serializeException(builder, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableConverter.convert(event)); + } else if (throwableProxy instanceof ThrowableProxy) { + LambdaJsonSerializer.serializeException(builder, ((ThrowableProxy) throwableProxy).getThrowable()); + } else { + LambdaJsonSerializer.serializeException(builder, throwableProxy.getClassName(), + throwableProxy.getMessage(), throwableProxyConverter.convert(event)); + } + } + LambdaJsonSerializer.serializePowertools(builder, event.getMDCPropertyMap(), includePowertoolsInfo); + if (includeThreadInfo) { + LambdaJsonSerializer.serializeThreadName(builder, event.getThreadName()); + LambdaJsonSerializer.serializeThreadId(builder, String.valueOf(Thread.currentThread().getId())); + LambdaJsonSerializer.serializeThreadPriority(builder, String.valueOf(Thread.currentThread().getPriority())); + } + LambdaJsonSerializer.serializeTimestamp(builder, event.getTimeStamp(), timestampFormat, + timestampFormatTimezoneId); + LambdaJsonSerializer.serializeObjectEnd(builder); + return builder.toString().getBytes(UTF_8); + } + + @Override + public byte[] footerBytes() { + return null; + } + + /** + * 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; + } + + /** + * Specify if messages should be logged as JSON, without escaping string (default is <b>false</b>): + * <br/> + * <pre>{@code + * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + * <logMessagesAsJson>true</logMessagesAsJson> + * </encoder> + * }</pre> + * + * @param logMessagesAsJson if messages should be looged as JSON (non escaped quotes) + */ + public void setLogMessagesAsJson(boolean logMessagesAsJson) { + this.logMessagesAsJsonGlobal = logMessagesAsJson; + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java new file mode 100644 index 000000000..e604d10c7 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java @@ -0,0 +1,130 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +/** + * Json tools to serialize attributes manually, to avoid using further dependencies (jackson, gson...) + */ +public class JsonUtils { + + private JsonUtils() { + // static utils + } + + protected static void serializeAttribute(StringBuilder builder, String attr, String value, boolean notBegin) { + if (value != null) { + if (notBegin) { + builder.append(","); + } + builder.append("\"").append(attr).append("\":"); + boolean isString = isString(value); + if (isString) { + builder.append("\""); + } + builder.append(value); + if (isString) { + builder.append("\""); + } + } + } + + protected static void serializeAttribute(StringBuilder builder, String attr, String value) { + serializeAttribute(builder, attr, value, true); + } + + protected static void serializeMessage(StringBuilder builder, String attr, String value, boolean logAsJson) { + builder.append(","); + builder.append("\"").append(attr).append("\":"); + if (logAsJson) { + builder.append(value); // log JSON without quotes + } else { + builder.append("\""); + builder.append(value.replace("\"", "\\\"")); // escape quotes in string + builder.append("\""); + } + } + + + protected static void serializeAttributeAsString(StringBuilder builder, String attr, String value, + boolean notBegin) { + if (value != null) { + if (notBegin) { + builder.append(","); + } + builder.append("\"") + .append(attr) + .append("\":\"") + .append(value) + .append("\""); + } + } + + protected static void serializeAttributeAsString(StringBuilder builder, String attr, String value) { + serializeAttributeAsString(builder, attr, value, true); + } + + /** + * 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/internal/LambdaEcsSerializer.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaEcsSerializer.java new file mode 100644 index 000000000..bab1a32fc --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaEcsSerializer.java @@ -0,0 +1,187 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 ch.qos.logback.classic.Level; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.regex.Matcher; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +/** + * This class will serialize the log events in ecs format (ElasticSearch).<br/> + * <p> + * Inspired from the ElasticSearch Serializer <code>co.elastic.logging.EcsJsonSerializer</code> + */ +public class LambdaEcsSerializer { + 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 SERVICE_ENV_ATTR_NAME = "service.environment"; + protected static final String EVENT_DATASET_ATTR_NAME = "event.dataset"; + protected static final String FORMATTED_MESSAGE_ATTR_NAME = "message"; + protected static final String THREAD_ATTR_NAME = "process.thread.name"; + protected static final String THREAD_ID_ATTR_NAME = "process.thread.id"; + 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"; + + private LambdaEcsSerializer() {} + + public static void serializeObjectStart(StringBuilder builder) { + builder.append('{'); + } + + public static void serializeObjectEnd(StringBuilder builder) { + builder.append("}\n"); + } + + public static void serializeTimestamp(StringBuilder builder, long timestamp, String timestampFormat, + String timestampFormatTimezoneId) { + 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); + } + JsonUtils.serializeAttributeAsString(builder, TIMESTAMP_ATTR_NAME, formattedTimestamp, false); + } + + public static void serializeThreadName(StringBuilder builder, String threadName) { + if (threadName != null) { + JsonUtils.serializeAttributeAsString(builder, THREAD_ATTR_NAME, threadName); + } + } + + public static void serializeLogLevel(StringBuilder builder, Level level) { + JsonUtils.serializeAttributeAsString(builder, LEVEL_ATTR_NAME, level.toString()); + } + + public static void serializeFormattedMessage(StringBuilder builder, String formattedMessage) { + JsonUtils.serializeAttributeAsString(builder, FORMATTED_MESSAGE_ATTR_NAME, + formattedMessage.replace("\"", Matcher.quoteReplacement("\\\""))); + } + + public static void serializeException(StringBuilder builder, String className, String message, String stackTrace) { + JsonUtils.serializeAttributeAsString(builder, EXCEPTION_MSG_ATTR_NAME, message); + JsonUtils.serializeAttributeAsString(builder, EXCEPTION_CLASS_ATTR_NAME, className); + JsonUtils.serializeAttributeAsString(builder, EXCEPTION_STACK_ATTR_NAME, stackTrace); + } + + public static void serializeException(StringBuilder builder, Throwable throwable) { + serializeException(builder, throwable.getClass().getName(), throwable.getMessage(), + Arrays.toString(throwable.getStackTrace())); + } + + public static void serializeThreadId(StringBuilder builder, String threadId) { + JsonUtils.serializeAttributeAsString(builder, THREAD_ID_ATTR_NAME, threadId); + } + + public static void serializeAdditionalFields(StringBuilder builder, Map<String, String> mdc) { + TreeMap<String, String> sortedMap = new TreeMap<>(mdc); + + sortedMap.forEach((k, v) -> { + if (!PowertoolsLoggedFields.stringValues().contains(k)) { + JsonUtils.serializeAttributeAsString(builder, k, v); + } + }); + } + + public static void serializeEcsVersion(StringBuilder builder, String ecsVersion) { + JsonUtils.serializeAttributeAsString(builder, ECS_VERSION_ATTR_NAME, ecsVersion); + } + + public static void serializeServiceName(StringBuilder builder, String serviceName) { + JsonUtils.serializeAttributeAsString(builder, SERVICE_NAME_ATTR_NAME, serviceName); + } + + public static void serializeServiceVersion(StringBuilder builder, String serviceVersion) { + JsonUtils.serializeAttributeAsString(builder, SERVICE_VERSION_ATTR_NAME, serviceVersion); + } + + public static void serializeLoggerName(StringBuilder builder, String loggerName) { + JsonUtils.serializeAttributeAsString(builder, LOGGER_ATTR_NAME, loggerName); + } + + public static void serializeCloudProvider(StringBuilder builder, String cloudProvider) { + JsonUtils.serializeAttributeAsString(builder, CLOUD_PROVIDER_ATTR_NAME, cloudProvider); + } + + public static void serializeCloudService(StringBuilder builder, String cloudService) { + JsonUtils.serializeAttributeAsString(builder, CLOUD_SERVICE_ATTR_NAME, cloudService); + } + + public static void serializeCloudRegion(StringBuilder builder, String cloudRegion) { + JsonUtils.serializeAttributeAsString(builder, CLOUD_REGION_ATTR_NAME, cloudRegion); + } + + public static void serializeCloudAccountId(StringBuilder builder, String cloudAccountId) { + JsonUtils.serializeAttributeAsString(builder, CLOUD_ACCOUNT_ATTR_NAME, cloudAccountId); + } + + public static void serializeColdStart(StringBuilder builder, String coldStart) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_COLD_START_ATTR_NAME, coldStart); + } + + public static void serializeFunctionExecutionId(StringBuilder builder, String requestId) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_REQUEST_ID_ATTR_NAME, requestId); + } + + public static void serializeFunctionId(StringBuilder builder, String functionArn) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_ARN_ATTR_NAME, functionArn); + } + + public static void serializeFunctionName(StringBuilder builder, String functionName) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_NAME_ATTR_NAME, functionName); + } + + public static void serializeFunctionVersion(StringBuilder builder, String functionVersion) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_VERSION_ATTR_NAME, functionVersion); + } + + public static void serializeFunctionMemory(StringBuilder builder, String functionMemory) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_MEMORY_ATTR_NAME, functionMemory); + } + + public static void serializeTraceId(StringBuilder builder, String traceId) { + JsonUtils.serializeAttributeAsString(builder, FUNCTION_TRACE_ID_ATTR_NAME, traceId); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java new file mode 100644 index 000000000..7d7b8d0d7 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java @@ -0,0 +1,150 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 static java.lang.Boolean.TRUE; +import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; + +import ch.qos.logback.classic.Level; +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; + +/** + * This class will serialize the log events in json.<br/> + * <p> + * Inspired from the ElasticSearch Serializer co.elastic.logging.EcsJsonSerializer + */ +public class LambdaJsonSerializer { + 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 LambdaJsonSerializer() {} + + public static void serializeObjectStart(StringBuilder builder) { + builder.append('{'); + } + + public static void serializeObjectEnd(StringBuilder builder) { + builder.append("}\n"); + } + + public static void serializeTimestamp(StringBuilder builder, long timestamp, String timestampFormat, + String timestampFormatTimezoneId) { + 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); + } + JsonUtils.serializeAttribute(builder, TIMESTAMP_ATTR_NAME, formattedTimestamp); + } + + public static void serializeThreadName(StringBuilder builder, String threadName) { + if (threadName != null) { + JsonUtils.serializeAttribute(builder, THREAD_ATTR_NAME, threadName); + } + } + + public static void serializeLogLevel(StringBuilder builder, Level level) { + JsonUtils.serializeAttribute(builder, LEVEL_ATTR_NAME, level.toString(), false); + } + + public static void serializeFormattedMessage(StringBuilder builder, String message, + boolean logMessagesAsJsonGlobal, String logMessagesAsJsonLocal) { + Boolean logMessagesAsJson = null; + if (logMessagesAsJsonLocal != null) { + logMessagesAsJson = Boolean.parseBoolean(logMessagesAsJsonLocal); + } + + boolean logAsJson = ((logMessagesAsJsonGlobal && logMessagesAsJson == null) || TRUE.equals(logMessagesAsJson)) + && isValidJson(message); + JsonUtils.serializeMessage(builder, FORMATTED_MESSAGE_ATTR_NAME, message, logAsJson); + } + + public static void serializeException(StringBuilder builder, String className, String message, String stackTrace) { + builder.append(",\"").append(EXCEPTION_ATTR_NAME).append("\":{"); + JsonUtils.serializeAttribute(builder, EXCEPTION_MSG_ATTR_NAME, message, false); + JsonUtils.serializeAttribute(builder, EXCEPTION_CLASS_ATTR_NAME, className); + JsonUtils.serializeAttribute(builder, EXCEPTION_STACK_ATTR_NAME, stackTrace); + builder.append("}"); + } + + public static void serializeException(StringBuilder builder, Throwable throwable) { + serializeException(builder, throwable.getClass().getName(), throwable.getMessage(), + Arrays.toString(throwable.getStackTrace())); + } + + public static void serializeThreadId(StringBuilder builder, String threadId) { + JsonUtils.serializeAttribute(builder, THREAD_ID_ATTR_NAME, threadId); + } + + public static void serializeThreadPriority(StringBuilder builder, String threadPriority) { + JsonUtils.serializeAttribute(builder, THREAD_PRIORITY_ATTR_NAME, threadPriority); + } + + public static void serializePowertools(StringBuilder builder, Map<String, String> mdc, + boolean includePowertoolsInfo) { + TreeMap<String, String> sortedMap = new TreeMap<>(mdc); + sortedMap.forEach((k, v) -> { + if ((includePowertoolsInfo || !PowertoolsLoggedFields.stringValues().contains(k)) // do not log already logged powertools info + && !(k.equals(PowertoolsLoggedFields.SAMPLING_RATE.getName()) && v.equals("0.0")) // do not log sampling rate when 0 + && !LOG_MESSAGES_AS_JSON.equals(k)) // do not log internal keys + { + JsonUtils.serializeAttribute(builder, k, v); + } + }); + + } + + private static final ObjectMapper mapper = new ObjectMapper() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + + private static boolean isValidJson(String str) { + if (!(str.startsWith("{") || str.startsWith("["))) { + return false; + } + try { + mapper.readTree(str); + } catch (JacksonException e) { + return false; + } + return true; + } + +} 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..86982b444 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackLoggingManager.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 software.amazon.lambda.powertools.logging.logback.internal; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import java.util.List; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.internal.LoggingManager; + +/** + * LoggingManager for Logback (see {@link LoggingManager}). + */ +public class LogbackLoggingManager implements LoggingManager { + + 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()); + } +} 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/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..214057917 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/LogbackLoggingManagerTest.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.logging; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.slf4j.event.Level.DEBUG; +import static org.slf4j.event.Level.ERROR; +import static org.slf4j.event.Level.WARN; + +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; +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); + + @Test + @Order(1) + 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 + @Order(2) + void resetLogLevel() { + LogbackLoggingManager manager = new LogbackLoggingManager(); + manager.setLogLevel(ERROR); + + Level logLevel = manager.getLogLevel(LOG); + assertThat(logLevel).isEqualTo(ERROR); + } +} 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..5dcca2fb2 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +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 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 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.mockito.Mock; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; + +@Order(3) +class LambdaEcsEncoderTest { + + private static final Logger logger = (Logger) LoggerFactory.getLogger(LambdaEcsEncoderTest.class.getName()); + + + @Mock + private Context context; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + openMocks(this); + MDC.clear(); + writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + setupContext(); + // 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{ + FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } + + @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\":\"eu-west-1\",\"cloud.account.id\":\"012345678910\",\"faas.id\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"faas.name\":\"testFunction\",\"faas.version\":\"1\",\"faas.memory\":\"1024\",\"faas.execution\":\"RequestId\",\"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:eu-west-1:012345678910:function:testFunction:1\",\"faas.name\":\"testFunction\",\"faas.version\":\"1\",\"faas.memory\":\"1024\",\"faas.execution\":\"RequestId\",\"faas.coldstart\":\"false\""); + + // 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\":\"eu-west-1\",\"cloud.account.id\":\"012345678910\""); + + // 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"); + } + + private void setupContext() { + when(context.getFunctionName()).thenReturn("testFunction"); + when(context.getInvokedFunctionArn()).thenReturn( + "arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1"); + when(context.getFunctionVersion()).thenReturn("1"); + when(context.getMemoryLimitInMB()).thenReturn(1024); + when(context.getAwsRequestId()).thenReturn("RequestId"); + } +} 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..dc8ac429b --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java @@ -0,0 +1,263 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +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 static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; + +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.core.JsonProcessingException; +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.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.TimeZone; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsJsonMessage; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +@Order(2) +class LambdaJsonEncoderTest { + private static final Logger logger = (Logger) LoggerFactory.getLogger(LambdaJsonEncoderTest.class.getName()); + + @Mock + private Context context; + + @BeforeAll + private static void init() { + JsonConfig.get().getObjectMapper().setSerializationInclusion(NON_NULL); + } + + @BeforeEach + void setUp() throws IllegalAccessException, IOException { + openMocks(this); + MDC.clear(); + writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + setupContext(); + // 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{ + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } + + @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:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"function_version\":1,\"myKey\":\"myValue\",\"service\":\"testLogback\",\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"timestamp\":"); + } + + @Test + void shouldLogJsonMessageWithoutEscapedStrings() { + // GIVEN + PowertoolsJsonMessage requestHandler = new PowertoolsJsonMessage(); + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("eu-west-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("\"message\":{\"messageId\":\"1212abcd\",\"body\":\"plop\",\"eventSource\":\"eb\",\"awsRegion\":\"eu-west-1\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}}}") + .contains("\"message\":\"1212abcd\"") + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .doesNotContain(LOG_MESSAGES_AS_JSON); + } + + private final LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "message", null, null); + + @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:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"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 shouldLogMessagesAsJsonWhenEnabledInLogbackConfig() throws JsonProcessingException { + // GIVEN + LambdaJsonEncoder encoder = new LambdaJsonEncoder(); + encoder.setLogMessagesAsJson(true); + + SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); + msg.setMessageId("1212abcd"); + msg.setBody("plop"); + msg.setEventSource("eb"); + msg.setAwsRegion("eu-west-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); + + // WHEN + LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, JsonConfig.get().getObjectMapper().writeValueAsString(msg), null, null); + byte[] encoded = encoder.encode(loggingEvent); + String result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (logged as JSON) + assertThat(result) + .contains("\"message\":{\"messageId\":\"1212abcd\",\"body\":\"plop\",\"eventSource\":\"eb\",\"awsRegion\":\"eu-west-1\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}}}"); + + // WHEN (disabling logging as json) + encoder.setLogMessagesAsJson(false); + encoded = encoder.encode(loggingEvent); + result = new String(encoded, StandardCharsets.UTF_8); + + // THEN (logged as String) + assertThat(result) + .contains("\"message\":\"{\\\"messageId\\\":\\\"1212abcd\\\",\\\"body\\\":\\\"plop\\\",\\\"eventSource\\\":\\\"eb\\\",\\\"awsRegion\\\":\\\"eu-west-1\\\",\\\"messageAttributes\\\":{\\\"keyAttribute\\\":{\\\"stringListValues\\\":[\\\"val1\\\",\\\"val2\\\",\\\"val3\\\"]}}}\""); + } + + @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\":1,\"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\":{\"message\":\"Unexpected value\",\"name\":\"java.lang.IllegalStateException\",\"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\":\"Error\",\"error\":{\"message\":\"Unexpected value\",\"name\":\"java.lang.IllegalStateException\",\"stack\":\"java.lang.IllegalStateException: Unexpected value\n"); + } + + private void setupContext() { + when(context.getFunctionName()).thenReturn("testFunction"); + when(context.getInvokedFunctionArn()).thenReturn( + "arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1"); + when(context.getFunctionVersion()).thenReturn("1"); + when(context.getMemoryLimitInMB()).thenReturn(1024); + when(context.getAwsRequestId()).thenReturn("RequestId"); + } +} diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java new file mode 100644 index 000000000..fdc279319 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.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.internal.handler; + +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.utilities.JsonConfig; + +public class PowertoolsJsonMessage implements RequestHandler<SQSEvent.SQSMessage, String> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsJsonMessage.class); + + @Override + @Logging(clearState = true) + public String handleRequest(SQSEvent.SQSMessage input, Context context) { + try { + LoggingUtils.logMessagesAsJson(true); + LoggingUtils.setCorrelationId(input.getMessageId()); + LOG.debug(JsonConfig.get().getObjectMapper().writeValueAsString(input)); + LOG.debug("{}", input.getMessageId()); + LOG.warn("Message body = {} and id = \"{}\"", input.getBody(), input.getMessageId()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + return input.getMessageId(); + } +} 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..faa722756 --- /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 software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; + +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) { + LoggingUtils.appendKey("myKey", "myValue"); + LOG.debug("Test debug event"); + return null; + } +} 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-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/spotbugs-exclude.xml b/powertools-logging/spotbugs-exclude.xml new file mode 100644 index 000000000..0437849ae --- /dev/null +++ b/powertools-logging/spotbugs-exclude.xml @@ -0,0 +1,30 @@ +<!-- 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="EI_EXPOSE_REP2"/> + <Or> + <And> + <Class name="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"/> + <Field name="throwableConverter"/> + </And> + <And> + <Class name="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"/> + <Field name="throwableConverter"/> + </And> + </Or> + </Match> + <!--False positive https://github.com/spotbugs/spotbugs/issues/1539--> + <Match> + <Bug pattern="DMI_RANDOM_USED_ONLY_ONCE"/> + <Class name="software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect"/> + <Method name="setLogLevelBasedOnSamplingRate"/> + </Match> +</FindBugsFilter> \ 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..05a9cfe31 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,35 @@ * <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> */ @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_RESPONE' 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; /** 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 index 6e11573cc..d7ceb8ccd 100644 --- 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 @@ -14,18 +14,22 @@ package software.amazon.lambda.powertools.logging; -import static java.util.Arrays.asList; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Arrays; import java.util.Map; -import org.apache.logging.log4j.ThreadContext; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.utilities.JsonConfig; /** - * A class of helper functions to add additional functionality to Logging. - * <p> - * {@see Logging} + * A class of helper functions to add functionality to Logging. + * Adding/removing keys is based on <a href="https://www.slf4j.org/manual.html#mdc">MDC</a>, which is ThreadSafe. */ public final class LoggingUtils { + + public static final String LOG_MESSAGES_AS_JSON = "PowertoolsLogMessagesAsJson"; + private static ObjectMapper objectMapper; private LoggingUtils() { @@ -39,7 +43,7 @@ private LoggingUtils() { * @param value The value to be logged */ public static void appendKey(String key, String value) { - ThreadContext.put(key, value); + MDC.put(key, value); } @@ -50,7 +54,7 @@ public static void appendKey(String key, String value) { * @param customKeys Map of custom keys values to be appended to logs */ public static void appendKeys(Map<String, String> customKeys) { - ThreadContext.putAll(customKeys); + customKeys.forEach(MDC::put); } /** @@ -59,7 +63,7 @@ public static void appendKeys(Map<String, String> customKeys) { * @param customKey The name of the key to be logged */ public static void removeKey(String customKey) { - ThreadContext.remove(customKey); + MDC.remove(customKey); } @@ -69,7 +73,7 @@ public static void removeKey(String customKey) { * @param keys Map of custom keys values to be appended to logs */ public static void removeKeys(String... keys) { - ThreadContext.removeAll(asList(keys)); + Arrays.stream(keys).forEach(MDC::remove); } /** @@ -78,24 +82,42 @@ public static void removeKeys(String... keys) { * @param value The value of the correlation id */ public static void setCorrelationId(String value) { - ThreadContext.put("correlation_id", value); + MDC.put(CORRELATION_ID.getName(), value); + } + + /** + * Get correlation id attribute. Maybe null. + * @return correlation id set `@Logging(correlationIdPath="JMESPATH Expression")` or `LoggingUtils.setCorrelationId("value")` + */ + public static String getCorrelationId() { + return MDC.get(CORRELATION_ID.getName()); + } + + /** + * When set to true, will log messages as JSON (without escaping string). + * Useful to log events or big JSON objects. + * @param value boolean to specify if yes or no messages should be logged as JSON (default is false) + */ + public static void logMessagesAsJson(boolean value) { + MDC.put(LOG_MESSAGES_AS_JSON, String.valueOf(value)); } /** * Sets the instance of ObjectMapper object which is used for serialising event when - * {@code @Logging(logEvent = true)}. + * {@code @Logging(logEvent = true, logResponse = true)}. + * + * Not Thread Safe, the object mapper is static, changing it in different threads can lead to unexpected behaviour * * @param objectMapper Custom implementation of object mapper to be used for logging serialised event */ - public static void defaultObjectMapper(ObjectMapper objectMapper) { + public static void setObjectMapper(ObjectMapper objectMapper) { LoggingUtils.objectMapper = objectMapper; } - public static ObjectMapper objectMapper() { - if (null == objectMapper) { - objectMapper = new ObjectMapper(); + public static ObjectMapper getObjectMapper() { + if (LoggingUtils.objectMapper == null) { + LoggingUtils.objectMapper = JsonConfig.get().getObjectMapper(); } - - return objectMapper; + return LoggingUtils.objectMapper; } } 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/DefautlLoggingManager.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefautlLoggingManager.java new file mode 100644 index 000000000..5326f53e6 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefautlLoggingManager.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 DefautlLoggingManager 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/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 2e17ce692..48f67700d 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 @@ -27,80 +27,146 @@ import static software.amazon.lambda.powertools.common.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.logging.internal.LoggingConstants.LAMBDA_LOG_LEVEL; +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_LEVEL; +import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_RESPONSE; +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.SERVICE; 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 io.burt.jmespath.Expression; 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.io.PrintStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; 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 java.util.ServiceLoader; 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.MDC; +import org.slf4j.MarkerFactory; +import org.slf4j.event.Level; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; +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 String POWERTOOLS_LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); - + private static final Logger LOG = LoggerFactory.getLogger(LambdaLoggingAspect.class); private static final Random SAMPLER = new Random(); - private static final String SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE"); - private static Boolean LOG_EVENT; + private static Level LEVEL_AT_INITIALISATION; /* not final for test purpose */ - private static Level LEVEL_AT_INITIALISATION; + private static final LoggingManager LOGGING_MANAGER; static { + LOGGING_MANAGER = getLoggingManagerFromServiceLoader(); + + setLogLevel(); + + LEVEL_AT_INITIALISATION = LOGGING_MANAGER.getLogLevel(LOG); + } + + static void setLogLevel() { if (POWERTOOLS_LOG_LEVEL != null) { - Level powertoolsLevel = Level.getLevel(POWERTOOLS_LOG_LEVEL); + Level powertoolsLevel = getLevelFromString(POWERTOOLS_LOG_LEVEL); if (LAMBDA_LOG_LEVEL != null) { - Level lambdaLevel = Level.getLevel(LAMBDA_LOG_LEVEL); - if (powertoolsLevel.intLevel() > lambdaLevel.intLevel()) { + 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); } } - resetLogLevels(powertoolsLevel); + setLogLevels(powertoolsLevel); } else if (LAMBDA_LOG_LEVEL != null) { - resetLogLevels(Level.getLevel(LAMBDA_LOG_LEVEL)); + setLogLevels(getLevelFromString(LAMBDA_LOG_LEVEL)); } + } - LEVEL_AT_INITIALISATION = LOG.getLevel(); + private static Level getLevelFromString(String level) { + if (Arrays.stream(Level.values()).anyMatch(slf4jLevel -> slf4jLevel.name().equalsIgnoreCase(level))) { + return Level.valueOf(level.toUpperCase()); + } else { + // FATAL does not exist in slf4j + if ("FATAL".equalsIgnoreCase(level)) { + return Level.ERROR; + } + } + // default to INFO if incorrect value + return Level.INFO; + } + + /** + * Use {@link ServiceLoader} to lookup for a {@link LoggingManager}. + * A file <i>software.amazon.lambda.powertools.logging.internal.LoggingManager</i> must be created in + * <i>META-INF/services/</i> folder with the appropriate implementation of the {@link LoggingManager} + * + * @return an instance of {@link LoggingManager} + * @throws IllegalStateException if no {@link LoggingManager} could be found + */ + @SuppressWarnings("java:S106") // S106: System.err is used rather than logger to make sure message is printed + private static LoggingManager getLoggingManagerFromServiceLoader() { + 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 getLoggingManager(loggingManagerList, System.err); + } - String logEvent = System.getenv("POWERTOOLS_LOGGER_LOG_EVENT"); - if (logEvent != null) { - LOG_EVENT = Boolean.parseBoolean(logEvent); + static LoggingManager getLoggingManager(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 DefautlLoggingManager(); } else { - LOG_EVENT = false; + 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 to your dependencies"); + printStream.println("WARN. Using the first LoggingManager found on the classpath: [" + loggingManagerList.get(0) + "]"); + } + loggingManager = loggingManagerList.get(0); } + return loggingManager; } - private static void resetLogLevels(Level logLevel) { - LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - Configurator.setAllLevels(LogManager.getRootLogger().getName(), logLevel); - ctx.updateLoggers(); + private static void setLogLevels(Level logLevel) { + LOGGING_MANAGER.setLogLevel(logLevel); } @SuppressWarnings({"EmptyMethod"}) @@ -108,41 +174,91 @@ private static void resetLogLevels(Level logLevel) { public void callAt(Logging logging) { } + /** + * 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(); + + boolean isOnRequestHandler = placedOnRequestHandler(pjp); + boolean isOnRequestStreamHandler = placedOnStreamHandler(pjp); setLogLevelBasedOnSamplingRate(pjp, logging); - Context extractedContext = extractContext(pjp); + addLambdaContextToLoggingContext(pjp); + + getXrayTraceId().ifPresent(xRayTraceId -> appendKey(FUNCTION_TRACE_ID.getName(), xRayTraceId)); + + Object[] proceedArgs = logEvent(pjp, logging, isOnRequestHandler, isOnRequestStreamHandler); + + if (!logging.correlationIdPath().isEmpty()) { + captureCorrelationId(logging.correlationIdPath(), proceedArgs, isOnRequestHandler, isOnRequestStreamHandler); + } - if (null != extractedContext) { - appendKeys(DefaultLambdaFields.values(extractedContext)); - appendKey("coldStart", isColdStart() ? "true" : "false"); - appendKey("service", serviceName()); + // 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 + OutputStream backupOutputStream = null; + if ((logging.logResponse() || POWERTOOLS_LOG_RESPONSE) && isOnRequestStreamHandler) { + backupOutputStream = (OutputStream) proceedArgs[1]; + proceedArgs[1] = new ByteArrayOutputStream(); } - getXrayTraceId().ifPresent(xRayTraceId -> appendKey("xray_trace_id", xRayTraceId)); + Object lambdaFunctionResponse; - // Check that the environment variable was enabled explicitly - // Or that the handler was annotated with @Logging(logEvent = true) - if (LOG_EVENT || logging.logEvent()) { - proceedArgs = logEvent(pjp); + try { + lambdaFunctionResponse = pjp.proceed(proceedArgs); + } catch (Throwable t) { + if (logging.logError() || POWERTOOLS_LOG_ERROR) { + // logging the exception with additional context + logger(pjp).error(MarkerFactory.getMarker("FATAL"), "Exception in Lambda Handler", t); + } + throw t; + } finally { + if (logging.clearState()) { + MDC.clear(); + } + coldStartDone(); } - if (!logging.correlationIdPath().isEmpty()) { - proceedArgs = captureCorrelationId(logging.correlationIdPath(), pjp); + 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); + } } - Object proceed = pjp.proceed(proceedArgs); + return lambdaFunctionResponse; + } + + private Object[] logEvent(ProceedingJoinPoint pjp, Logging logging, + boolean isOnRequestHandler, boolean isOnRequestStreamHandler) { + Object[] proceedArgs = pjp.getArgs(); - if (logging.clearState()) { - ThreadContext.clearMap(); + if (logging.logEvent() || POWERTOOLS_LOG_EVENT) { + if (isOnRequestHandler) { + logRequestHandlerEvent(pjp, pjp.getArgs()[0]); + } else if (isOnRequestStreamHandler) { + proceedArgs = logRequestStreamHandlerEvent(pjp); + } } + return proceedArgs; + } + + private void addLambdaContextToLoggingContext(ProceedingJoinPoint pjp) { + Context extractedContext = extractContext(pjp); - coldStartDone(); - return proceed; + if (extractedContext != null) { + appendKeys(PowertoolsLoggedFields.setValuesFromLambdaContext(extractedContext)); + appendKey(FUNCTION_COLD_START.getName(), isColdStart() ? "true" : "false"); + appendKey(SERVICE.getName(), serviceName()); + } } private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, @@ -152,12 +268,12 @@ private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, if (isHandlerMethod(pjp)) { if (samplingRate < 0 || samplingRate > 1) { - LOG.debug("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", + LOG.warn("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", samplingRate); return; } - appendKey("samplingRate", String.valueOf(samplingRate)); + appendKey(PowertoolsLoggedFields.SAMPLING_RATE.getName(), String.valueOf(samplingRate)); if (samplingRate == 0) { return; @@ -166,130 +282,132 @@ private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, float sample = SAMPLER.nextFloat(); if (samplingRate > sample) { - resetLogLevels(Level.DEBUG); + setLogLevels(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); + LOG.debug("Changed log level to DEBUG based on Sampling configuration. " + + "Sampling Rate: {}, Sampler Value: {}.", samplingRate, sample); + } else if (LEVEL_AT_INITIALISATION != LOGGING_MANAGER.getLogLevel(LOG)) { + setLogLevels(LEVEL_AT_INITIALISATION); } } } private double samplingRate(final Logging logging) { - if (null != SAMPLING_RATE) { + String sampleRate = POWERTOOLS_SAMPLING_RATE; + if (null != sampleRate) { try { - return Double.parseDouble(SAMPLING_RATE); + return Double.parseDouble(sampleRate); } catch (NumberFormatException e) { - LOG.debug("Skipping sampling rate on environment variable configuration because of invalid " + - "value. Sampling rate: {}", SAMPLING_RATE); + LOG.warn("Skipping sampling rate on environment variable configuration because of invalid " + + "value. Sampling rate: {}", sampleRate); } } 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); - } + private void logRequestHandlerEvent(final ProceedingJoinPoint pjp, final Object event) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + LoggingUtils.logMessagesAsJson(true); + asJson(event).ifPresent(log::info); + LoggingUtils.logMessagesAsJson(false); + } + } - if (placedOnStreamHandler(pjp)) { - args = logFromInputStream(pjp); + private Object[] logRequestStreamHandlerEvent(final ProceedingJoinPoint pjp) { + Object[] args = pjp.getArgs(); + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + LoggingUtils.logMessagesAsJson(true); + try { + byte[] bytes = bytesFromInputStreamSafely((InputStream) pjp.getArgs()[0]); + args[0] = new ByteArrayInputStream(bytes); + // do not log asJson as it can be something else (String, XML...) + log.info("{}", new String(bytes, UTF_8)); + } catch (IOException e) { + LOG.warn("Failed to log event from supplied input stream.", e); } + LoggingUtils.logMessagesAsJson(false); } - 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; - } + private void logRequestHandlerResponse(final ProceedingJoinPoint pjp, final Object response) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + LoggingUtils.logMessagesAsJson(true); + asJson(response).ifPresent(log::info); + LoggingUtils.logMessagesAsJson(false); + } + } - if (placedOnStreamHandler(pjp)) { - try { - byte[] bytes = bytesFromInputStreamSafely((InputStream) pjp.getArgs()[0]); - JsonNode jsonNode = objectMapper().readTree(bytes); - args[0] = new ByteArrayInputStream(bytes); + private void logRequestStreamHandlerResponse(final ProceedingJoinPoint pjp, final byte[] bytes) { + Logger log = logger(pjp); + if (log.isInfoEnabled()) { + LoggingUtils.logMessagesAsJson(true); + // we do not log with asJson as it can be something else (String, XML, ...) + log.info("{}", new String(bytes, UTF_8)); + LoggingUtils.logMessagesAsJson(false); + } + } - setCorrelationIdFromNode(correlationIdPath, pjp, jsonNode); + private void captureCorrelationId(final String correlationIdPath, + Object[] proceedArgs, + final boolean isOnRequestHandler, + final boolean isOnRequestStreamHandler) { + if (isOnRequestHandler) { + JsonNode jsonNode = LoggingUtils.getObjectMapper().valueToTree(proceedArgs[0]); + setCorrelationIdFromNode(correlationIdPath, jsonNode); + } else if (isOnRequestStreamHandler) { + try { + byte[] bytes = bytesFromInputStreamSafely((InputStream) proceedArgs[0]); + JsonNode jsonNode = LoggingUtils.getObjectMapper().readTree(bytes); + proceedArgs[0] = new ByteArrayInputStream(bytes); - return args; - } catch (IOException e) { - Logger log = logger(pjp); - log.warn("Failed to capture correlation id on event from supplied input stream.", e); - } + setCorrelationIdFromNode(correlationIdPath, jsonNode); + } catch (IOException e) { + LOG.warn("Failed to capture correlation id on event from supplied input stream.", e); } } - - return args; } - private void setCorrelationIdFromNode(String correlationIdPath, ProceedingJoinPoint pjp, JsonNode jsonNode) { - JsonNode node = jsonNode.at(JsonPointer.compile(correlationIdPath)); + private void setCorrelationIdFromNode(String correlationIdPath, JsonNode jsonNode) { + Expression<JsonNode> jmesExpression = JsonConfig.get().getJmesPath().compile(correlationIdPath); + JsonNode node = jmesExpression.search(jsonNode); 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?"); + LOG.warn("Unable to extract any correlation id. Is your function expecting supported event type?"); } } - 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); - } - - return args; - } private byte[] bytesFromInputStreamSafely(final InputStream inputStream) throws IOException { try (ByteArrayOutputStream out = new ByteArrayOutputStream(); - InputStreamReader reader = new InputStreamReader(inputStream, UTF_8)) { + InputStreamReader reader = new InputStreamReader(inputStream, UTF_8)) { OutputStreamWriter writer = new OutputStreamWriter(out, UTF_8); - - IOUtils.copy(reader, writer); + int n; + char[] buffer = new char[4096]; + while (-1 != (n = reader.read(buffer))) { + writer.write(buffer, 0, n); + } writer.flush(); return out.toByteArray(); } } - private Optional<String> asJson(final ProceedingJoinPoint pjp, - final Object target) { + private Optional<String> asJson(final Object target) { try { - return ofNullable(objectMapper().writeValueAsString(target)); + return ofNullable(LoggingUtils.getObjectMapper().writeValueAsString(target)); } catch (JsonProcessingException e) { - logger(pjp).error("Failed logging event of type {}", target.getClass(), e); + LOG.error("Failed logging object of type {}", target.getClass(), e); return empty(); } } 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/LambdaTimestampResolver.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java deleted file mode 100644 index 500b36c95..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java +++ /dev/null @@ -1,169 +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 software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_FORMAT; -import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LOG_DATE_RFC3339_FORMAT; - -import java.util.Locale; -import java.util.TimeZone; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.time.MutableInstant; -import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolver; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig; -import org.apache.logging.log4j.layout.template.json.util.InstantFormatter; -import org.apache.logging.log4j.layout.template.json.util.JsonWriter; - -/** - * Default timestamp used by log4j is not RFC3339, which is used by Lambda internally to filter logs. - * When `AWS_LAMBDA_LOG_FORMAT` is set to JSON (i.e. using Lambda logging configuration), we should use the appropriate pattern, - * otherwise logs with invalid date format are considered as INFO. - * Inspired from org.apache.logging.log4j.layout.template.json.resolver.TimestampResolver - * - * TODO: remove in v2 an replace with the good pattern in LambdaJsonLayout.json - */ -public class LambdaTimestampResolver implements EventResolver { - - private final EventResolver internalResolver; - - public LambdaTimestampResolver(final TemplateResolverConfig config) { - final PatternResolverContext patternResolverContext = - PatternResolverContext.fromConfig(config); - internalResolver = new PatternResolver(patternResolverContext); - } - - @Override - public void resolve(LogEvent value, JsonWriter jsonWriter) { - internalResolver.resolve(value, jsonWriter); - } - - static String getName() { - return "lambda-timestamp"; - } - - private static final class PatternResolverContext { - - public static final String PATTERN = "pattern"; - private final InstantFormatter formatter; - - private final StringBuilder lastFormattedInstantBuffer = new StringBuilder(); - - private final MutableInstant lastFormattedInstant = new MutableInstant(); - - private PatternResolverContext( - final String pattern, - final TimeZone timeZone, - final Locale locale) { - this.formatter = InstantFormatter - .newBuilder() - .setPattern(pattern) - .setTimeZone(timeZone) - .setLocale(locale) - .build(); - lastFormattedInstant.initFromEpochSecond(-1, 0); - } - - private static PatternResolverContext fromConfig( - final TemplateResolverConfig config) { - final String pattern = readPattern(config); - final TimeZone timeZone = readTimeZone(config); - final Locale locale = config.getLocale(new String[]{PATTERN, "locale"}); - return new PatternResolverContext(pattern, timeZone, locale); - } - - private static String readPattern(final TemplateResolverConfig config) { - final String format = config.getString(new String[]{PATTERN, "format"}); - return format != null - ? format - : getLambdaTimestampFormatOrDefault(); - } - - private static String getLambdaTimestampFormatOrDefault() { - return "JSON".equals(LAMBDA_LOG_FORMAT) ? LOG_DATE_RFC3339_FORMAT : - JsonTemplateLayoutDefaults.getTimestampFormatPattern(); - } - - private static TimeZone readTimeZone(final TemplateResolverConfig config) { - final String timeZoneId = config.getString(new String[]{PATTERN, "timeZone"}); - if (timeZoneId == null) { - return JsonTemplateLayoutDefaults.getTimeZone(); - } - boolean found = false; - for (final String availableTimeZone : TimeZone.getAvailableIDs()) { - if (availableTimeZone.equalsIgnoreCase(timeZoneId)) { - found = true; - break; - } - } - if (!found) { - throw new IllegalArgumentException( - "invalid timestamp time zone: " + config); - } - return TimeZone.getTimeZone(timeZoneId); - } - - } - - private static final class PatternResolver implements EventResolver { - - private final PatternResolverContext patternResolverContext; - - private PatternResolver(final PatternResolverContext patternResolverContext) { - this.patternResolverContext = patternResolverContext; - } - - @Override - public synchronized void resolve( - final LogEvent logEvent, - final JsonWriter jsonWriter) { - - // Format timestamp if it doesn't match the last cached one. - final boolean instantMatching = patternResolverContext.formatter.isInstantMatching( - patternResolverContext.lastFormattedInstant, - logEvent.getInstant()); - if (!instantMatching) { - - // Format the timestamp. - patternResolverContext.lastFormattedInstantBuffer.setLength(0); - patternResolverContext.lastFormattedInstant.initFrom(logEvent.getInstant()); - patternResolverContext.formatter.format( - patternResolverContext.lastFormattedInstant, - patternResolverContext.lastFormattedInstantBuffer); - - // Write the formatted timestamp. - final StringBuilder jsonWriterStringBuilder = jsonWriter.getStringBuilder(); - final int startIndex = jsonWriterStringBuilder.length(); - jsonWriter.writeString(patternResolverContext.lastFormattedInstantBuffer); - - // Cache the written value. - patternResolverContext.lastFormattedInstantBuffer.setLength(0); - patternResolverContext.lastFormattedInstantBuffer.append( - jsonWriterStringBuilder, - startIndex, - jsonWriterStringBuilder.length()); - - } - - // Write the cached formatted timestamp. - else { - jsonWriter.writeRawString( - patternResolverContext.lastFormattedInstantBuffer); - } - - } - - } -} diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java deleted file mode 100644 index 2022c6d4a..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.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 software.amazon.lambda.powertools.logging.internal; - -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; - -@Plugin(name = "LambdaTimestampResolverFactory", category = TemplateResolverFactory.CATEGORY) -public final class LambdaTimestampResolverFactory implements EventResolverFactory { - - private static final LambdaTimestampResolverFactory INSTANCE = new LambdaTimestampResolverFactory(); - - private LambdaTimestampResolverFactory() { - } - - @PluginFactory - public static LambdaTimestampResolverFactory getInstance() { - return INSTANCE; - } - - @Override - public String getName() { - return LambdaTimestampResolver.getName(); - } - - @Override - public TemplateResolver<LogEvent> create(EventResolverContext context, - TemplateResolverConfig config) { - return new LambdaTimestampResolver(config); - } -} 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 index e58ca4109..989608a77 100644 --- 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 @@ -14,12 +14,18 @@ package software.amazon.lambda.powertools.logging.internal; -public class LoggingConstants { - public static final String LAMBDA_LOG_LEVEL = System.getenv("AWS_LAMBDA_LOG_LEVEL"); +class LoggingConstants { + static String LAMBDA_LOG_LEVEL = System.getenv("AWS_LAMBDA_LOG_LEVEL"); /* not final for test purpose */ - public static final String LAMBDA_LOG_FORMAT = System.getenv("AWS_LAMBDA_LOG_FORMAT"); + static String POWERTOOLS_LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); /* not final for test purpose */ - public static final String LOG_DATE_RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + 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/DefaultLambdaFields.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java similarity index 59% 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..6e0047f4f 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()); + } + + 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 da3385032..000000000 --- a/powertools-logging/src/main/resources/LambdaJsonLayout.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "timestamp": { - "$resolver": "lambda-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/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 95fb9c47f..000000000 --- a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java +++ /dev/null @@ -1,173 +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.as; -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.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.time.format.ResolverStyle; -import java.util.Map; -import org.apache.logging.log4j.Level; -import org.assertj.core.api.InstanceOfAssertFactories; -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 shouldLogWithRFC3339TimestampFormat_WhenLambdaLoggingIsJSON() throws Exception { - // Given: AWS_LAMBDA_LOG_FORMAT=JSON defined in pom.xml - - // When - handler.handleRequest("test", context); - - // Then - assertThat(Files.lines(Paths.get("target/logfile.json"))) - .hasSize(1) - .allSatisfy(line -> assertThat(parseToMap(line)) - .extracting("timestamp", as(InstanceOfAssertFactories.STRING)) - .satisfies(s -> assertThat(hasDateFormat(s, "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")).isTrue())); - } - - private boolean hasDateFormat(String timestamp, String format) { - DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT); - try { - dtf.parse(timestamp); - return true; - } catch (DateTimeParseException e) { - return false; - } - } - - @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..2a9322592 --- /dev/null +++ b/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java @@ -0,0 +1,452 @@ +/** + * 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; + + /** + * 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) { + + 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; + } + +} 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 index 8889fb93c..04e977c58 100644 --- 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 @@ -16,73 +16,99 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; 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; +import org.slf4j.MDC; +import software.amazon.lambda.powertools.utilities.JsonConfig; class LoggingUtilsTest { @BeforeEach void setUp() { - ThreadContext.clearAll(); + MDC.clear(); } @Test - void shouldSetCustomKeyOnThreadContext() { - LoggingUtils.appendKey("test", "value"); + void shouldSetCustomKeyInLoggingContext() { + LoggingUtils.appendKey("org/slf4j/test", "value"); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(1) - .containsEntry("test", "value"); + .containsEntry("org/slf4j/test", "value"); } @Test - void shouldSetCustomKeyAsMapOnThreadContext() { + void shouldSetCustomKeyAsMapInLoggingContext() { Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); + customKeys.put("org/slf4j/test", "value"); customKeys.put("test1", "value1"); LoggingUtils.appendKeys(customKeys); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(2) - .containsEntry("test", "value") + .containsEntry("org/slf4j/test", "value") .containsEntry("test1", "value1"); } @Test - void shouldRemoveCustomKeyOnThreadContext() { - LoggingUtils.appendKey("test", "value"); + void shouldRemoveCustomKeyInLoggingContext() { + LoggingUtils.appendKey("org/slf4j/test", "value"); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(1) - .containsEntry("test", "value"); + .containsEntry("org/slf4j/test", "value"); - LoggingUtils.removeKey("test"); + LoggingUtils.removeKey("org/slf4j/test"); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .isEmpty(); } @Test - void shouldRemoveCustomKeysOnThreadContext() { + void shouldRemoveCustomKeysInLoggingContext() { Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); + customKeys.put("org/slf4j/test", "value"); customKeys.put("test1", "value1"); LoggingUtils.appendKeys(customKeys); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(2) - .containsEntry("test", "value") + .containsEntry("org/slf4j/test", "value") .containsEntry("test1", "value1"); - LoggingUtils.removeKeys("test", "test1"); + LoggingUtils.removeKeys("org/slf4j/test", "test1"); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .isEmpty(); } + + @Test + void shouldAddCorrelationIdToLoggingContext() { + String id = "correlationID_12345"; + LoggingUtils.setCorrelationId(id); + + assertThat(MDC.getCopyOfContextMap()) + .hasSize(1) + .containsEntry("correlation_id", id); + + assertThat(LoggingUtils.getCorrelationId()).isEqualTo(id); + } + + @Test + void shouldGetObjectMapper() { + assertThat(LoggingUtils.getObjectMapper()).isNotNull(); + assertThat(LoggingUtils.getObjectMapper()).isEqualTo(JsonConfig.get().getObjectMapper()); + + ObjectMapper mapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + LoggingUtils.setObjectMapper(mapper); + assertThat(LoggingUtils.getObjectMapper()).isEqualTo(mapper); + + } } \ No newline at end of file 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 67% 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..e1829a777 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 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) { + LoggingUtils.appendKey("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..d6c79a445 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabled.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.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 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"); + } +} 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/PowerToolLogEventEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java similarity index 92% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java index 8a960fa87..87677d601 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java @@ -18,10 +18,10 @@ 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-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/PowerToolLogEventDisabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventDisabled.java similarity index 89% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventDisabled.java index 77103e450..fc1feb52d 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventDisabled.java @@ -18,9 +18,9 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.logging.Logging; -public class PowerToolLogEventDisabled implements RequestHandler<Object, Object> { +public class PowertoolsLogEventDisabled implements RequestHandler<Object, Object> { - @Logging(logEvent = false) + @Logging @Override 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/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java similarity index 80% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java rename to powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java index 9de76586f..350b29cde 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java @@ -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/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..001bde3ed --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/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.handlers; + +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/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..38be5c025 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/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.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 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/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogSamplingDisabled.java similarity index 68% 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/PowertoolsLogSamplingDisabled.java index df68ea14f..5e2a7f148 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/PowertoolsLogSamplingDisabled.java @@ -16,23 +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 PowerLogToolEnabled implements RequestHandler<Object, Object> { - private final Logger LOG = LogManager.getLogger(PowerLogToolEnabled.class); +public class PowertoolsLogSamplingDisabled implements RequestHandler<Object, Boolean> { + private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogSamplingDisabled.class); @Override - @Logging - public Object handleRequest(Object input, Context context) { + @Logging(samplingRate = 0.0) + public Boolean handleRequest(Object input, Context context) { LOG.info("Test event"); LOG.debug("Test debug event"); - return null; - } - - @Logging - public void anotherMethod() { - System.out.println("test"); + 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/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index 453e1de29..bc5e53675 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,18 +14,24 @@ 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.assertj.core.api.Assertions.contentOf; 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.common.internal.SystemWrapper.getProperty; import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; +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; @@ -33,52 +39,61 @@ 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.events.SQSEvent; 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.io.PrintStream; +import java.io.UnsupportedEncodingException; 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.ArrayList; +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 org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; 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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.event.Level; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.common.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.PowerToolLogEventDisabled; -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 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.PowertoolsLogEvent; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventDisabled; +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 { + private static final Logger LOG = LoggerFactory.getLogger(LambdaLoggingAspectTest.class); private static final int EXPECTED_CONTEXT_SIZE = 8; private RequestStreamHandler requestStreamHandler; private RequestHandler<Object, Object> requestHandler; @@ -87,210 +102,327 @@ class LambdaLoggingAspectTest { private Context context; @BeforeEach - void setUp() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { + void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException { openMocks(this); - ThreadContext.clearAll(); + MDC.clear(); writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); setupContext(); - requestHandler = new PowerLogToolEnabled(); - requestStreamHandler = new PowerLogToolEnabledForStream(); + requestHandler = new PowertoolsLogEnabled(); + requestStreamHandler = new PowertoolsLogEnabledForStream(); + resetLogLevel(Level.INFO); + 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); + 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 FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - resetLogLevel(Level.INFO); + } + + @Test + void shouldLogDebugWhenPowertoolsLevelEnvVarIsDebug() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "DEBUG", true); + + // WHEN + LambdaLoggingAspect.setLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isTrue(); + } + + @Test + void shouldLogInfoWhenPowertoolsLevelEnvVarIsInfo() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INFO", true); + + // WHEN + LambdaLoggingAspect.setLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isTrue(); + } + + @Test + void shouldLogInfoWhenPowertoolsLevelEnvVarIsInvalid() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INVALID", true); + + // WHEN + LambdaLoggingAspect.setLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isTrue(); + } + + @Test + void shouldLogWarnWhenPowertoolsLevelEnvVarIsWarn() throws IllegalAccessException { + // GIVEN + writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "WARN", true); + + // WHEN + LambdaLoggingAspect.setLogLevel(); + + // 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 + LambdaLoggingAspect.setLogLevel(); + + // 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 + LambdaLoggingAspect.setLogLevel(); + + // 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 + LambdaLoggingAspect.setLogLevel(); + + // 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 + LambdaLoggingAspect.setLogLevel(); + + // 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 + LambdaLoggingAspect.setLogLevel(); + + // THEN + assertThat(LOG.isDebugEnabled()).isFalse(); + assertThat(LOG.isInfoEnabled()).isFalse(); + assertThat(LOG.isWarnEnabled()).isTrue(); } @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(), "testArn") + .containsEntry(FUNCTION_MEMORY_SIZE.getName(), "10") + .containsEntry(FUNCTION_VERSION.getName(), "1") + .containsEntry(FUNCTION_NAME.getName(), "testFunction") + .containsEntry(FUNCTION_REQUEST_ID.getName(), "RequestId") + .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(), "testArn") + .containsEntry(FUNCTION_MEMORY_SIZE.getName(), "10") + .containsEntry(FUNCTION_VERSION.getName(), "1") + .containsEntry(FUNCTION_NAME.getName(), "testFunction") + .containsEntry(FUNCTION_REQUEST_ID.getName(), "RequestId") + .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 shouldHaveNoEffectIfNotUsedOnLambdaHandler() { - PowerLogToolEnabled handler = new PowerLogToolEnabled(); + void shouldClearStateWhenClearStateIsTrue() { + PowertoolsLogClearState handler = new PowertoolsLogClearState(); - handler.anotherMethod(); + handler.handleRequest(Collections.singletonMap("mySuperSecret", "P@ssw0Rd"), context); - assertThat(ThreadContext.getImmutableContext()) - .isEmpty(); + assertThat(MDC.getCopyOfContextMap()).isNull(); } @Test - void shouldLogEventForHandler() throws IOException, JSONException { - requestHandler = new PowerToolLogEventEnabled(); - S3EventNotification s3EventNotification = s3EventNotification(); - - requestHandler.handleRequest(s3EventNotification, context); + void shouldLogDebugWhenSamplingEqualsOne() { + PowertoolsLogSamplingEnabled handler = new PowertoolsLogSamplingEnabled(); - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + Boolean debugEnabled = handler.handleRequest(new Object(), context); - String event = (String) log.get("message"); - - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) - .lines().collect(joining("\n")); - - assertEquals(expectEvent, event, false); + assertThat(debugEnabled).isTrue(); } - /** - * If POWERTOOLS_LOGGER_LOG_EVENT was set to true, the handler should log, despite @Logging(logEvent=false) - * - * @throws IOException - */ @Test - void shouldLogEventForHandlerWhenEnvVariableSetToTrue() throws IOException, IllegalAccessException, JSONException { - try { - writeStaticField(LambdaLoggingAspect.class, "LOG_EVENT", Boolean.TRUE, true); - - requestHandler = new PowerToolLogEventDisabled(); - S3EventNotification s3EventNotification = s3EventNotification(); - - requestHandler.handleRequest(s3EventNotification, context); + void shouldLogDebugWhenSamplingEnvVarEqualsOne() throws IllegalAccessException { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "1"; + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + // WHEN + handler.handleRequest(new Object(), context); - String event = (String) log.get("message"); - - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) - .lines().collect(joining("\n")); - - assertEquals(expectEvent, event, false); - } finally { - writeStaticField(LambdaLoggingAspect.class, "LOG_EVENT", Boolean.FALSE, true); - } + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Test debug event"); } - /** - * If POWERTOOLS_LOGGER_LOG_EVENT was set to false and @Logging(logEvent=false), the handler shouldn't log - * - * @throws IOException - */ @Test - void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() throws IOException { - requestHandler = new PowerToolLogEventDisabled(); - S3EventNotification s3EventNotification = s3EventNotification(); + void shouldNotLogDebugWhenSamplingEnvVarIsTooBig() throws IllegalAccessException { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "42"; - requestHandler.handleRequest(s3EventNotification, context); + // WHEN + requestHandler.handleRequest(new Object(), context); - Assertions.assertEquals(0, - Files.lines(Paths.get("target/logfile.json")).collect(joining()).length()); + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).doesNotContain("Test debug event"); } @Test - void shouldLogEventForHandlerWithOverriddenObjectMapper() throws IOException, JSONException { - RequestHandler<S3EventNotification, Object> handler = new PowerToolLogEventEnabledWithCustomMapper(); - S3EventNotification s3EventNotification = s3EventNotification(); - - handler.handleRequest(s3EventNotification, context); - - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + void shouldNotLogDebugWhenSamplingEnvVarIsInvalid() { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "NotANumber"; - String event = (String) log.get("message"); - - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/customizedLogEvent.json"))) - .lines().collect(joining("\n")); + // WHEN + requestHandler.handleRequest(new Object(), context); - assertEquals(expectEvent, event, false); + // 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 shouldLogEventForStreamAndLambdaStreamIsValid() throws IOException, JSONException { - requestStreamHandler = new PowerToolLogEventEnabledForStream(); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - S3EventNotification s3EventNotification = s3EventNotification(); + void shouldNotLogDebugWhenSamplingEqualsZero() { + // GIVEN + LoggingConstants.POWERTOOLS_SAMPLING_RATE = "0"; + PowertoolsLogSamplingDisabled handler = new PowertoolsLogSamplingDisabled(); - requestStreamHandler.handleRequest( - new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); - - assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) - .isNotEmpty(); + // WHEN + Boolean debugEnabled = handler.handleRequest(new Object(), context); - Map<String, Object> log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + // THEN + assertThat(debugEnabled).isFalse(); + } - String event = (String) log.get("message"); + @Test + void shouldHaveNoEffectIfNotUsedOnLambdaHandler() { + // GIVEN + PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); - String expectEvent = new BufferedReader( - new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) - .lines().collect(joining("\n")); + // WHEN + handler.anotherMethod(); - assertEquals(expectEvent, event, false); + // THEN + assertThat(MDC.getCopyOfContextMap()).isNull(); } @Test void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { + // GIVEN writeStaticField(LambdaHandlerProcessor.class, "SERVICE_NAME", "testService", true); + + // WHEN requestHandler.handleRequest(new Object(), context); - assertThat(ThreadContext.getImmutableContext()) + // THEN + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry("service", "testService"); + .containsEntry(SERVICE.getName(), "testService"); } @Test @@ -305,7 +437,7 @@ void shouldLogxRayTraceIdSystemPropertySet() { requestHandler.handleRequest(new Object(), context); - assertThat(ThreadContext.getImmutableContext()) + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE + 1) .containsEntry("xray_trace_id", xRayTraceId); } @@ -313,27 +445,191 @@ void shouldLogxRayTraceIdSystemPropertySet() { @Test void shouldLogxRayTraceIdEnvVarSet() { + // GIVEN String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); + // WHEN requestHandler.handleRequest(new Object(), context); - assertThat(ThreadContext.getImmutableContext()) + // THEN + assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE + 1) - .containsEntry("xray_trace_id", xRayTraceId); + .containsEntry(FUNCTION_TRACE_ID.getName(), xRayTraceId); + } + } + + @Test + void shouldLogEventForHandlerWithLogEventAnnotation() { + // GIVEN + requestHandler = new PowertoolsLogEvent(); + + // WHEN + requestHandler.handleRequest(singletonList("ListOfOneElement"), context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("[\"ListOfOneElement\"]"); + } + + @Test + void shouldLogEventForHandlerWhenEnvVariableSetToTrue() throws IllegalAccessException { + try { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = true; + + requestHandler = new PowertoolsLogEnabled(); + + SQSEvent.SQSMessage message = new SQSEvent.SQSMessage(); + message.setBody("body"); + message.setMessageId("1234abcd"); + message.setAwsRegion("eu-west-1"); + + // WHEN + requestHandler.handleRequest(message, context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("\"body\":\"body\"").contains("\"messageId\":\"1234abcd\"").contains("\"awsRegion\":\"eu-west-1\""); + } finally { + LoggingConstants.POWERTOOLS_LOG_EVENT = false; + } + } + + @Test + void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() throws IOException { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = false; + + // WHEN + requestHandler = new PowertoolsLogEventDisabled(); + requestHandler.handleRequest(singletonList("ListOfOneElement"), context); + + // THEN + Assertions.assertEquals(0, + Files.lines(Paths.get("target/logfile.json")).collect(joining()).length()); + } + + @Test + void shouldLogEventForStreamHandler() throws IOException { + // GIVEN + 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("{\"key\":\"value\"}"); + } + + @Test + void shouldLogResponseForHandlerWithLogResponseAnnotation() { + // GIVEN + requestHandler = new PowertoolsLogResponse(); + + // WHEN + requestHandler.handleRequest("input", context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Hola mundo"); + } + + @Test + void shouldLogResponseForHandlerWhenEnvVariableSetToTrue() throws IllegalAccessException { + try { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_RESPONSE = true; + + requestHandler = new PowertoolsLogEnabled(); + + // WHEN + requestHandler.handleRequest("input", context); + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("Bonjour le monde"); + } finally { + LoggingConstants.POWERTOOLS_LOG_RESPONSE = false; + } + } + + @Test + 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); + + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains(input); + } + + @Test + void shouldLogErrorForHandlerWithLogErrorAnnotation() { + // GIVEN + requestHandler = new PowertoolsLogError(); + + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + + // THEN + File logFile = new File("target/logfile.json"); + assertThat(contentOf(logFile)).contains("This is an error"); + } + + @Test + void shouldLogErrorForHandlerWhenEnvVariableSetToTrue() throws IllegalAccessException { + 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()); } @@ -341,10 +637,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()); } @@ -352,46 +652,92 @@ 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); + } - assertThat(ThreadContext.getImmutableContext()) + @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); + + // 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 testMultipleLoggingManagers_shouldWarnAndSelectFirstOne() throws UnsupportedEncodingException { + // GIVEN + List<LoggingManager> list = new ArrayList<>(); + list.add(new TestLoggingManager()); + list.add(new DefautlLoggingManager()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(outputStream); + + // WHEN + LambdaLoggingAspect.getLoggingManager(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 to your dependencies") + .contains("WARN. Using the first LoggingManager found on the classpath: [" + list.get(0) + "]"); + } - requestHandler.handleRequest(s3EventNotification, context); - requestHandler.handleRequest(s3EventNotification, context); + @Test + void testNoLoggingManagers_shouldWarnAndCreateDefault() throws UnsupportedEncodingException { + // GIVEN + List<LoggingManager> list = new ArrayList<>(); - List<String> logLines = Files.lines(Paths.get("target/logfile.json")).collect(Collectors.toList()); - Map<String, Object> invokeLog = parseToMap(logLines.get(0)); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(outputStream); - assertThat(invokeLog) - .containsEntry("TestKey", "TestValue"); + // WHEN + LoggingManager loggingManager = LambdaLoggingAspect.getLoggingManager(list, stream); - invokeLog = parseToMap(logLines.get(1)); + // 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(invokeLog) - .doesNotContainKey("TestKey"); + assertThat(loggingManager).isExactlyInstanceOf(DefautlLoggingManager.class); } private void setupContext() { @@ -404,44 +750,9 @@ private void setupContext() { private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); - resetLogLevels.setAccessible(true); - resetLogLevels.invoke(null, level); + Method setLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("setLogLevels", Level.class); + setLogLevels.setAccessible(true); + setLogLevels.invoke(null, level); writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); } - - 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 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(); - } - } } \ No newline at end of file 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..0958e0d3b --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/TestLoggingManager.java @@ -0,0 +1,32 @@ +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 { + + private final TestLoggerFactory loggerFactory; + + public TestLoggingManager() { + ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); + if (!(loggerFactory instanceof TestLoggerFactory)) { + throw new RuntimeException( + "LoggerFactory does not match required type: " + TestLoggerFactory.class.getName()); + } + this.loggerFactory = (TestLoggerFactory) loggerFactory; + } + + @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()); + } +} 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/log4j2.xml b/powertools-logging/src/test/resources/log4j2.xml deleted file mode 100644 index 22a44ee8b..000000000 --- a/powertools-logging/src/test/resources/log4j2.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration> - <Appenders> - <File name="JsonAppender" fileName="target/logfile.json"> - <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> - </File> - </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/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 8c9ce79ad..7de1efa3f 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -27,7 +27,7 @@ <version>2.0.0-SNAPSHOT</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. @@ -56,6 +56,11 @@ </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 1bc662457..54065aab2 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -26,11 +26,16 @@ <artifactId>powertools-parameters</artifactId> - <name>Powertools for AWS Lambda (Java) library Parameters</name> + <name>Powertools for AWS Lambda (Java) - Parameters</name> <description>Set of utilities to retrieve parameters - common interface</description> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index d92d68fb0..d1b2de826 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -27,7 +27,7 @@ <artifactId>powertools-serialization</artifactId> <packaging>jar</packaging> - <name>Powertools for AWS Lambda (Java) library Serialization Utilities</name> + <name>Powertools for AWS Lambda (Java) - Serialization Utilities</name> <description> </description> @@ -64,8 +64,8 @@ <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> 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..e961f21fa 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 @@ -82,6 +82,6 @@ public <T extends BaseFunction> void addFunction(T function) { } private static class ConfigHolder { - private final static JsonConfig instance = new JsonConfig(); + private static final JsonConfig instance = new JsonConfig(); } } diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 6498d3772..1625fd0cb 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -27,7 +27,7 @@ <version>2.0.0-SNAPSHOT</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> @@ -56,6 +56,11 @@ </distributionManagement> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 269513a46..bd57fa6c5 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -27,7 +27,7 @@ <version>2.0.0-SNAPSHOT</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> 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 143f0584d..ccc5a4c2c 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 @@ -97,6 +97,6 @@ public ObjectMapper getObjectMapper() { } private static class ConfigHolder { - private final static ValidationConfig instance = new ValidationConfig(); + private static final ValidationConfig instance = new ValidationConfig(); } } diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index dc22f22b6..2d61cc68d 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -46,22 +46,6 @@ <Match> <Bug pattern="EI_EXPOSE_REP"/> <Or> - <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields"/> - <Method name="getLogEvent"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaJsonLayout$LogEventWithAdditionalFields"/> - <Method name="getAdditionalFields"/> - </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.idempotency.Idempotency"/> <Method name="getPersistenceStore"/> @@ -80,20 +64,12 @@ <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"/> - </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"/> @@ -130,12 +106,21 @@ </Or> </Match> <!--Functionally needed--> + <Match> + <Bug pattern="MS_SHOULD_BE_FINAL"/> + <Or> + <And> + <Class name="software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect"/> + <Field name="SAMPLING_RATE"/> + </And> + </Or> + </Match> <Match> <Bug pattern="EI_EXPOSE_STATIC_REP2"/> <Or> <And> <Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/> - <Method name="defaultObjectMapper"/> + <Method name="setObjectMapper"/> </And> <And> <Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/> @@ -156,7 +141,7 @@ <Or> <And> <Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/> - <Method name="objectMapper"/> + <Method name="getObjectMapper"/> </And> <And> <Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/> From bb9bb2e540b58938caf29a46731f4fedc8fea155 Mon Sep 17 00:00:00 2001 From: Jason Harris <jiharris90@gmail.com> Date: Tue, 12 Dec 2023 10:51:10 +0000 Subject: [PATCH 0543/1008] chore: SAM and Terraform IaC extracted from pr_build and simplified approach. (#1533) * SAM and Terraform IaC extracted from pr_build and simplified approach. * Update .github/workflows/pr_iac_lint.yml Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --------- Co-authored-by: Jason Harris <harrzjas@amazon.co.uk> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- .github/workflows/pr_build.yml | 21 ------------- .github/workflows/pr_iac_lint.yml | 49 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/pr_iac_lint.yml diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 3cf3a5425..7a02b08d4 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -79,33 +79,12 @@ jobs: if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 working-directory: examples/powertools-examples-core/kotlin run: ./gradlew build - - name: Setup Terraform - if: ${{ matrix.java == '11' }} - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - name: Setup AWS credentials if: ${{ matrix.java == '11' }} uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_REGION }} - - name: Terraform validate - working-directory: examples/powertools-examples-core/terraform - if: ${{ matrix.java == '11' }} - run: | - terraform -version - terraform init -backend=false - terraform validate - terraform plan - - name: Setup Terraform lint - if: ${{ matrix.java == '11' }} - uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - - name: Terraform lint - working-directory: examples/powertools-examples-core/terraform - if: ${{ matrix.java == '11' }} - run: | - tflint --version - tflint --init - tflint -f compact - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml new file mode 100644 index 000000000..1ed2077f5 --- /dev/null +++ b/.github/workflows/pr_iac_lint.yml @@ -0,0 +1,49 @@ +name: Validate IaC + +on: + push: + branches: + - main + - v2 + pull_request: + branches: + - main + - v2 + paths: + - 'examples/**' +jobs: + linter: + runs-on: ubuntu-latest + strategy: + matrix: + project: ["sam", "gradle", "kotlin"] + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java JDK + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: 11 + - name: Run SAM validator to check syntax of IaC templates - Java + working-directory: examples/powertools-examples-core/${{ matrix.project }} + run: | + sam build + sam validate --lint + - name: Setup Terraform + uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 + - name: Run Terraform validator to check syntax of IaC templates and produce a plan of changes + working-directory: examples/powertools-examples-core/terraform + run: | + mvn install + terraform -version + terraform init -backend=false + terraform validate + terraform plan + - name: Setup Terraform lint + uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 + - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. + working-directory: examples/powertools-examples-core/terraform + run: | + tflint --version + tflint --init + tflint -f compact \ No newline at end of file From 516db4e25b21891cb03fd63eb00c25a6bb0a2b5d Mon Sep 17 00:00:00 2001 From: jdoherty <jdoherty07@gmail.com> Date: Thu, 14 Dec 2023 06:52:42 +0000 Subject: [PATCH 0544/1008] chore: cleanup poms and reduce warning noise (#1535) * pom cleanup and reduce warning noise * added variable for version --- examples/pom.xml | 1 + examples/powertools-examples-batch/pom.xml | 1 + .../pom.xml | 1 + .../cdk/app/pom.xml | 1 + .../cdk/infra/pom.xml | 1 + .../terraform/pom.xml | 1 + .../powertools-examples-idempotency/pom.xml | 1 + .../powertools-examples-parameters/pom.xml | 1 + .../powertools-examples-serialization/pom.xml | 1 + .../powertools-examples-validation/pom.xml | 1 + pom.xml | 40 ++++++++++--------- powertools-cloudformation/pom.xml | 22 ---------- powertools-common/pom.xml | 23 ----------- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 22 ---------- powertools-large-messages/pom.xml | 22 ---------- powertools-logging/pom.xml | 5 ++- powertools-metrics/pom.xml | 22 ---------- powertools-parameters/pom.xml | 1 - powertools-serialization/pom.xml | 22 ---------- powertools-tracing/pom.xml | 23 ----------- powertools-validation/pom.xml | 23 ----------- .../validation/internal/ValidationAspect.java | 7 ++-- 23 files changed, 38 insertions(+), 206 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 943cad950..f864c3c04 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -48,6 +48,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index a1b4c0bbc..947f146b3 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -136,6 +136,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index bee97e52a..8851daa6d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -144,6 +144,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index f8d340f3b..f964782e3 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -132,6 +132,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 053bd239c..f4d82141b 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -34,6 +34,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index c6f838619..94f79a5fd 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -118,6 +118,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 5a040fec0..19d7a2272 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -176,6 +176,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 3630811f3..a08747ff2 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -108,6 +108,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 2c8fc951a..106fe7cbb 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -41,6 +41,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index ed33568cb..1c39ee8a0 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -106,6 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/pom.xml b/pom.xml index b7227cf8a..7577659f4 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,25 @@ <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> + <licenses> <license> <name>Apache License, Version 2.0</name> @@ -63,21 +82,11 @@ <module>powertools-parameters/powertools-parameters-tests</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> + <maven.deploy.plugin.version>3.1.1</maven.deploy.plugin.version> + <log4j.version>2.20.0</log4j.version> <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.15.3</jackson.version> @@ -103,13 +112,6 @@ <elastic.version>1.5.0</elastic.version> </properties> - <distributionManagement> - <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> - </snapshotRepository> - </distributionManagement> - <dependencyManagement> <dependencies> <dependency> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 79071d532..e3e4748d6 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -32,28 +32,6 @@ 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> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 363d2e944..15409e1f6 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -29,29 +29,6 @@ <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> - <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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index a64e7a354..9d84ce9f2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -184,7 +184,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>${maven.deploy.plugin.version}</version> <configuration> <skip>true</skip> </configuration> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index eda7bd982..fd53cd9e2 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -31,28 +31,6 @@ <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> <dependencies> <dependency> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index af031ff21..4206183de 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -31,28 +31,6 @@ <name>Powertools for AWS Lambda (Java) - 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> - <dependencies> <dependency> <groupId>org.aspectj</groupId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 56ca0e62b..43c65a3b0 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -17,16 +17,17 @@ 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.0.0-SNAPSHOT</version> </parent> - <artifactId>powertools-logging</artifactId> - <packaging>jar</packaging> <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> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 7de1efa3f..0daa49664 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -32,28 +32,6 @@ 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> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 54065aab2..6c90e30a8 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -27,7 +27,6 @@ <artifactId>powertools-parameters</artifactId> <name>Powertools for AWS Lambda (Java) - Parameters</name> - <description>Set of utilities to retrieve parameters - common interface</description> <dependencies> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index d1b2de826..454b30d3e 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -31,28 +31,6 @@ <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> <dependencies> <dependency> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 1625fd0cb..9d60d4f40 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -31,29 +31,6 @@ <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> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index bd57fa6c5..a5fc4a890 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -31,29 +31,6 @@ <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> 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 978be16de..bcbba9e03 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 @@ -22,10 +22,6 @@ import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; -import java.util.Collections; -import java.util.List; -import java.util.Map; - import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; @@ -47,6 +43,9 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.networknt.schema.JsonSchema; +import java.util.Collections; +import java.util.List; +import java.util.Map; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; From fb14bcfee1aae8e03b8c8081b2478edefc9b5b87 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:46:10 +0100 Subject: [PATCH 0545/1008] chore: Support spotbugs running anywhere (#1537) * Allow spotbugs & checkstyle to run from any subdirectory * Clean up spotbugs-exclude --- .mvn/README | 2 + checkstyle.xml | 2 +- examples/spotbugs-exclude.xml | 20 ------ pom.xml | 7 +- powertools-logging/spotbugs-exclude.xml | 30 -------- powertools-parameters/spotbugs-exclude.xml | 82 ---------------------- spotbugs-exclude.xml | 65 ++++++++++++++++- 7 files changed, 71 insertions(+), 137 deletions(-) create mode 100644 .mvn/README delete mode 100644 examples/spotbugs-exclude.xml delete mode 100644 powertools-logging/spotbugs-exclude.xml delete mode 100644 powertools-parameters/spotbugs-exclude.xml 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/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/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/pom.xml b/pom.xml index 7577659f4..a4df4116a 100644 --- a/pom.xml +++ b/pom.xml @@ -110,6 +110,10 @@ <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> <elastic.version>1.5.0</elastic.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> <dependencyManagement> @@ -544,7 +548,7 @@ </executions> <configuration> <xmlOutput>true</xmlOutput> - <excludeFilterFile>../spotbugs-exclude.xml</excludeFilterFile> + <excludeFilterFile>${project.rootdir}/spotbugs-exclude.xml</excludeFilterFile> </configuration> </plugin> </plugins> @@ -641,6 +645,7 @@ <artifactId>maven-checkstyle-plugin</artifactId> <version>3.3.0</version> <configuration> + <propertyExpansion>basedir=${project.rootdir}</propertyExpansion> <configLocation>checkstyle.xml</configLocation> <consoleOutput>true</consoleOutput> <failsOnError>true</failsOnError> diff --git a/powertools-logging/spotbugs-exclude.xml b/powertools-logging/spotbugs-exclude.xml deleted file mode 100644 index 0437849ae..000000000 --- a/powertools-logging/spotbugs-exclude.xml +++ /dev/null @@ -1,30 +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="EI_EXPOSE_REP2"/> - <Or> - <And> - <Class name="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"/> - <Field name="throwableConverter"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"/> - <Field name="throwableConverter"/> - </And> - </Or> - </Match> - <!--False positive https://github.com/spotbugs/spotbugs/issues/1539--> - <Match> - <Bug pattern="DMI_RANDOM_USED_ONLY_ONCE"/> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect"/> - <Method name="setLogLevelBasedOnSamplingRate"/> - </Match> -</FindBugsFilter> \ No newline at end of file diff --git a/powertools-parameters/spotbugs-exclude.xml b/powertools-parameters/spotbugs-exclude.xml deleted file mode 100644 index d48e9bee1..000000000 --- a/powertools-parameters/spotbugs-exclude.xml +++ /dev/null @@ -1,82 +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="EI_EXPOSE_REP2"/> - <Or> - <And> - <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.parameters.transform.TransformationManager"/> - <Field name="transformation"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.BaseProvider"/> - <Field name="transformationManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> - <Field name="transformationManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> - <Field name="cacheManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> - <Field name="transformationManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> - <Field name="client"/> - </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.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.parameters.dynamodb.DynamoDbProviderBuilder"/> - <Field name="cacheManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> - <Field name="transformationManager"/> - </And> - <And> - <Class name="software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProviderBuilder"/> - <Field name="client"/> - </And> - - </Or> - </Match> -</FindBugsFilter> \ No newline at end of file diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 2d61cc68d..747752130 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -39,7 +39,34 @@ <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> </Or> </Match> <!-- Internals of Log event for apache log4j--> @@ -58,6 +85,18 @@ <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> @@ -103,6 +142,26 @@ <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> <!--Functionally needed--> @@ -171,5 +230,5 @@ <Method name="threadOption1"/> </And> </Or> - </Match> -</FindBugsFilter> \ No newline at end of file + </Match> +</FindBugsFilter> From f625b334ec55c679e3bf0c513e8cb865a192efdd Mon Sep 17 00:00:00 2001 From: ritigupt <102658810+ritigupt@users.noreply.github.com> Date: Tue, 2 Jan 2024 18:19:24 +0530 Subject: [PATCH 0546/1008] Removing LambdaJsonLayout from logging in examples (#1545) --- .../src/main/resources/log4j2.xml | 2 +- .../src/main/resources/log4j2.xml | 2 +- powertools-tracing/src/test/resources/log4j2.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-parameters/src/main/resources/log4j2.xml b/examples/powertools-examples-parameters/src/main/resources/log4j2.xml index 033da8a11..fe943d707 100644 --- a/examples/powertools-examples-parameters/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-parameters/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-serialization/src/main/resources/log4j2.xml b/examples/powertools-examples-serialization/src/main/resources/log4j2.xml index 033da8a11..fe943d707 100644 --- a/examples/powertools-examples-serialization/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-serialization/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/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> From 47fab11f33d05e14e75415315f5f8f7600b9a308 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 3 Jan 2024 09:07:11 +0100 Subject: [PATCH 0547/1008] chore: Remove empty CDK test (#1542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove CDK test * Build core utilities so that sam validator can find them * Remove CDK test * Ok just build it all * Remove the plan ... * Remove terraform plan from iac lint --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- .github/workflows/pr_iac_lint.yml | 5 +- .../infra/src/test/java/cdk/CdkStackTest.java | 48 ------------------- 2 files changed, 4 insertions(+), 49 deletions(-) delete mode 100644 examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml index 1ed2077f5..c6e17ab1c 100644 --- a/.github/workflows/pr_iac_lint.yml +++ b/.github/workflows/pr_iac_lint.yml @@ -24,6 +24,10 @@ jobs: with: distribution: 'corretto' java-version: 11 + - name: Build Project + working-directory: . + run: | + mvn install -DskipTests - name: Run SAM validator to check syntax of IaC templates - Java working-directory: examples/powertools-examples-core/${{ matrix.project }} run: | @@ -38,7 +42,6 @@ jobs: terraform -version terraform init -backend=false terraform validate - terraform plan - name: Setup Terraform lint uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. 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); - } -} From e7933c23bc33767d6e8887e8e0d5c36a5d0f7a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:16:04 +0100 Subject: [PATCH 0548/1008] dependabot on v2 branch (#1548) --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1454df79c..28adf2bc1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,3 +11,12 @@ updates: # Ignore Mockito 5.X.X as it does not support Java 8 - dependency-name: "org.mockito:mockito-*" update-types: ["version-update:semver-major"] + + - package-ecosystem: "maven" + directory: "/" + target-branch: "v2" + schedule: + interval: "weekly" + labels: + - "maven" + - "dependencies" From 3963d6d1f58c9618959f7a7044189a4a32d9b3fc Mon Sep 17 00:00:00 2001 From: ritigupt <102658810+ritigupt@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:21:37 +0530 Subject: [PATCH 0549/1008] fix(v2): Fix params builder to provide default transformation manager (#1549) * Add default transformation manager in builders and add unit tests * undo the pom change * undo the pom change --- .../appconfig/AppConfigProviderBuilder.java | 4 +++- .../parameters/appconfig/AppConfigProviderTest.java | 13 +++++++++++++ .../dynamodb/DynamoDbProviderBuilder.java | 3 +++ .../parameters/dynamodb/DynamoDbProviderTest.java | 11 +++++++++++ .../parameters/secrets/SecretsProviderBuilder.java | 4 +++- .../parameters/secrets/SecretsProviderTest.java | 13 +++++++++++++ .../parameters/ssm/SSMProviderBuilder.java | 3 +++ .../powertools/parameters/ssm/SSMProviderTest.java | 12 ++++++++++++ 8 files changed, 61 insertions(+), 2 deletions(-) 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 index dadacb843..b6f6cd809 100644 --- 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 @@ -51,7 +51,9 @@ public AppConfigProvider build() { 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() diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java index ded568e8d..039611875 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java @@ -17,7 +17,9 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.assertj.core.api.Assertions.assertThat; +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 org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -216,4 +218,15 @@ public void testAppConfigProviderBuilderMissingApplication_throwsException() { .build()) .withMessage("No application provided; please provide one"); } + @Test + public 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-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 index 6b6610ba1..b98ff285d 100644 --- 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 @@ -61,6 +61,9 @@ public DynamoDbProvider build() { if (client == null) { client = createClient(); } + if (transformationManager == null) { + transformationManager = new TransformationManager(); + } provider = new DynamoDbProvider(cacheManager, transformationManager, client, table); return provider; diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 68bfd7cdb..10d756c69 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -16,7 +16,9 @@ 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; @@ -225,5 +227,14 @@ public void testDynamoDBBuilderMissingTable_throwsException() { .withCacheManager(new CacheManager()) .build()); } + @Test + public void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { + + // Act + DynamoDbProvider dynamoDbProvider = DynamoDbProvider.builder().withTable("test-table") + .build(); + // Assert + assertDoesNotThrow(()->dynamoDbProvider.withTransformation(json)); + } } 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 index 125425200..c5806689f 100644 --- 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 @@ -59,7 +59,9 @@ public SecretsProvider build() { if (client == null) { client = createClient(); } - + if(transformationManager == null){ + transformationManager = new TransformationManager(); + } provider = new SecretsProvider(cacheManager, transformationManager, client); return provider; diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java index 21173cad1..a2607dd2c 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java @@ -16,10 +16,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatRuntimeException; +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.mockito.ArgumentCaptor; @@ -105,4 +108,14 @@ public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() assertNotNull(secretsProvider); assertNotNull(secretsProvider.getClient()); } + + @Test + public void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + + // Act + SecretsProvider secretsProvider = SecretsProvider.builder() + .build(); + // Assert + assertDoesNotThrow(()->secretsProvider.withTransformation(json)); + } } 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 index 3b4fff1b3..4c26463fb 100644 --- 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 @@ -57,6 +57,9 @@ public SSMProvider build() { client = createClient(); } + if(transformationManager == null){ + transformationManager = new TransformationManager(); + } provider = new SSMProvider(cacheManager, transformationManager, client); return provider; diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index b105da438..db45dc21c 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -16,6 +16,8 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +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; @@ -194,6 +196,16 @@ public void getMultipleWithNextToken() { assertThat(request2.nextToken()).isEqualTo("123abc"); } + @Test + public void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + + // Act + SSMProvider ssmProvider = SSMProvider.builder() + .build(); + // Assert + assertDoesNotThrow(()->ssmProvider.withTransformation(json)); + } + private void initMock(String expectedValue) { Parameter parameter = Parameter.builder().value(expectedValue).build(); GetParameterResponse result = GetParameterResponse.builder().parameter(parameter).build(); From b0af349a64d04732f989ace73a76f7b7a9d7b1fa Mon Sep 17 00:00:00 2001 From: Subhash Kovela <46015546+subhash686@users.noreply.github.com> Date: Tue, 13 Feb 2024 06:26:09 -0500 Subject: [PATCH 0550/1008] Maintenance: Upgraded jackson and aws xray versions (#1556) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a4df4116a..f0f5e051e 100644 --- a/pom.xml +++ b/pom.xml @@ -89,9 +89,9 @@ <log4j.version>2.20.0</log4j.version> <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> - <jackson.version>2.15.3</jackson.version> + <jackson.version>2.16.0</jackson.version> <aws.sdk.version>2.21.0</aws.sdk.version> - <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> + <aws.xray.recorder.version>2.15.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> From 82d4b308778b4408359a99797af648f20889bbf4 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 13 Feb 2024 15:50:40 +0100 Subject: [PATCH 0551/1008] chore(v2): Split powertools idempotency module (without redis impl) (#1559) * Introducing sub-modules in the idempotency module * Updating the idempotency documentation * Move test resource to the dynamodb idempotency sub-module * Renaming common module to core and refactoring package name for repo implementation * fix spotbugs for multiple modules and new pr_artifacts * fix spotbugs for multiple modules * fix spotbugs for multiple modules - with pom property * fix spotbugs for multiple modules - multiple spotbugs files * Try fixing java8 build * fix aspectj weaving * Clean-up pom dependencies and remove awssdk#StringUtils usage * Fix sonar issues * Fix some merge issues --------- Co-authored-by: Eleni Dimitropoulou <12170229+eldimi@users.noreply.github.com> Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> --- .github/workflows/pr_artifacts_size.yml | 3 +- .github/workflows/pr_build.yml | 1 + docs/utilities/idempotency.md | 12 +- .../powertools-examples-idempotency/pom.xml | 4 +- .../src/main/java/helloworld/App.java | 2 +- pom.xml | 20 --- powertools-batch/pom.xml | 1 + powertools-idempotency/pom.xml | 68 ++-------- .../powertools-idempotency-core/pom.xml | 42 ++++++ .../powertools/idempotency/Constants.java | 0 .../powertools/idempotency/Idempotency.java | 0 .../idempotency/IdempotencyConfig.java | 5 +- .../idempotency/IdempotencyKey.java | 0 .../powertools/idempotency/Idempotent.java | 1 + ...IdempotencyAlreadyInProgressException.java | 0 .../IdempotencyConfigurationException.java | 0 ...IdempotencyInconsistentStateException.java | 0 ...IdempotencyItemAlreadyExistsException.java | 0 .../IdempotencyItemNotFoundException.java | 0 .../exceptions/IdempotencyKeyException.java | 0 .../IdempotencyPersistenceLayerException.java | 0 .../IdempotencyValidationException.java | 0 .../internal/IdempotencyHandler.java | 11 +- .../internal/IdempotentAspect.java | 9 +- .../idempotency/internal/cache/LRUCache.java | 0 .../persistence/BasePersistenceStore.java | 38 +++--- .../idempotency/persistence/DataRecord.java | 3 +- .../persistence/PersistenceStore.java | 3 +- .../handlers/IdempotencyEnabledFunction.java | 0 .../handlers/IdempotencyInternalFunction.java | 0 ...dempotencyInternalFunctionInternalKey.java | 0 .../IdempotencyInternalFunctionInvalid.java | 0 .../IdempotencyInternalFunctionVoid.java | 0 .../handlers/IdempotencyStringFunction.java | 0 .../IdempotencyWithErrorFunction.java | 0 .../internal/IdempotencyAspectTest.java | 29 ++-- .../internal/cache/LRUCacheTest.java | 4 +- .../powertools/idempotency/model/Basket.java | 0 .../powertools/idempotency/model/Product.java | 0 .../persistence/BasePersistenceStoreTest.java | 15 ++- .../src/test/resources/apigw_event.json | 0 .../powertools-idempotency-dynamodb/pom.xml | 126 ++++++++++++++++++ .../dynamodb}/DynamoDBPersistenceStore.java | 31 +++-- .../DynamoDBConfig.java | 2 +- .../IdempotencyTest.java | 4 +- .../handlers/IdempotencyFunction.java | 21 +-- .../DynamoDBPersistenceStoreTest.java | 22 +-- .../src/test/resources/apigw_event2.json | 0 powertools-idempotency/spotbugs-exclude.xml | 46 +++++++ .../powertools-parameters-appconfig/pom.xml | 6 +- .../powertools-parameters-dynamodb/pom.xml | 5 + .../powertools-parameters-secrets/pom.xml | 5 + .../powertools-parameters-ssm/pom.xml | 5 + powertools-validation/pom.xml | 5 + spotbugs-exclude.xml | 10 +- 55 files changed, 379 insertions(+), 180 deletions(-) create mode 100644 powertools-idempotency/powertools-idempotency-core/pom.xml rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java (96%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyStringFunction.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyWithErrorFunction.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java (100%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java (99%) rename powertools-idempotency/{ => powertools-idempotency-core}/src/test/resources/apigw_event.json (100%) create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/pom.xml rename powertools-idempotency/{src/main/java/software/amazon/lambda/powertools/idempotency/persistence => powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/DynamoDBPersistenceStore.java (98%) rename powertools-idempotency/{src/test/java/software/amazon/lambda/powertools/idempotency => powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb}/DynamoDBConfig.java (98%) rename powertools-idempotency/{src/test/java/software/amazon/lambda/powertools/idempotency => powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb}/IdempotencyTest.java (93%) rename powertools-idempotency/{src/test/java/software/amazon/lambda/powertools/idempotency => powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb}/handlers/IdempotencyFunction.java (93%) rename powertools-idempotency/{src/test/java/software/amazon/lambda/powertools/idempotency/persistence => powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/DynamoDBPersistenceStoreTest.java (99%) rename powertools-idempotency/{ => powertools-idempotency-dynamodb}/src/test/resources/apigw_event2.json (100%) create mode 100644 powertools-idempotency/spotbugs-exclude.xml diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml index 1d905af01..2244f7b06 100644 --- a/.github/workflows/pr_artifacts_size.yml +++ b/.github/workflows/pr_artifacts_size.yml @@ -11,7 +11,8 @@ on: - 'powertools-core/**' # not in v2 - 'powertools-common/**' # v2 only - 'powertools-e2e-tests/**' - - 'powertools-idempotency/**' + - 'powertools-idempotency-core/**' + - 'powertools-idempotency-dynamodb/**' - 'powertools-large-messages/**' - 'powertools-logging/**' - 'powertools-metrics/**' diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index e86f49753..5373f4b4c 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -26,6 +26,7 @@ on: push: branches: - main + - v2 paths: - 'powertools-batch/**' - 'powertools-cloudformation/**' diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 5392b8d4c..f4defbdfd 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -35,7 +35,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> <version>{{ powertools.version }}</version> </dependency> ... @@ -56,7 +56,7 @@ 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-dynamodb</artifactId> </aspectLibrary> </aspectLibraries> </configuration> @@ -80,7 +80,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl ... <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> <version>{{ powertools.version }}</version> </dependency> ... @@ -101,7 +101,7 @@ 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-dynamodb</artifactId> </aspectLibrary> </aspectLibraries> </configuration> @@ -131,7 +131,7 @@ 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-dynamodb:{{ powertools.version }}' } sourceCompatibility = 11 // or higher @@ -151,7 +151,7 @@ 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-dynamodb:{{ powertools.version }}' } sourceCompatibility = 1.8 diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 19d7a2272..39d6a8172 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -41,7 +41,7 @@ </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -95,7 +95,7 @@ </aspectLibrary> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> </aspectLibrary> </aspectLibraries> </configuration> diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index cf0c0ee31..0c4693230 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -32,7 +32,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; import software.amazon.lambda.powertools.utilities.JsonConfig; diff --git a/pom.xml b/pom.xml index f0f5e051e..0c7ca43d3 100644 --- a/pom.xml +++ b/pom.xml @@ -32,25 +32,6 @@ <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> - <licenses> <license> <name>Apache License, Version 2.0</name> @@ -421,7 +402,6 @@ </configuration> <executions> <execution> - <phase>process-sources</phase> <goals> <goal>compile</goal> <goal>test-compile</goal> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index eaafdb56e..1886f56e6 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -25,6 +25,7 @@ </build> <artifactId>powertools-batch</artifactId> + <dependencies> <dependency> <groupId>com.amazonaws</groupId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index fd53cd9e2..fddef497a 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -25,13 +25,19 @@ </parent> <artifactId>powertools-idempotency</artifactId> - <packaging>jar</packaging> + <packaging>pom</packaging> - <name>Powertools for AWS Lambda (Java) - Idempotency</name> + <name>Powertools for AWS Lambda (Java) library Idempotency</name> <description> </description> + + <modules> + <module>powertools-idempotency-core</module> + <module>powertools-idempotency-dynamodb</module> + </modules> + <dependencies> <dependency> <groupId>org.aspectj</groupId> @@ -42,34 +48,6 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> </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> - <!-- Test dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> @@ -106,20 +84,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> @@ -150,22 +114,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> </project> \ No newline at end of file diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml new file mode 100644 index 000000000..302cc24f5 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -0,0 +1,42 @@ +<?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.0.0-SNAPSHOT</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 --> + </dependencies> +</project> \ No newline at end of file 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/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 similarity index 100% rename from powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java rename to powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java 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 99% 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..2b22cac51 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 @@ -15,9 +15,10 @@ package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; -import java.time.Duration; import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import java.time.Duration; + /** * Configuration of the idempotency feature. Use the {@link Builder} to create an instance. */ @@ -92,7 +93,7 @@ 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; 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 100% 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 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 99% 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..7982d911a 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 @@ -14,13 +14,8 @@ package software.amazon.lambda.powertools.idempotency.internal; -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 org.slf4j.Logger; @@ -37,6 +32,12 @@ import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; +import java.time.Instant; +import java.util.OptionalInt; + +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; + /** * 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. 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 99% 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 0b9d729f4..ea6d743f0 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,12 +14,8 @@ package software.amazon.lambda.powertools.idempotency.internal; -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; @@ -33,6 +29,11 @@ import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; import software.amazon.lambda.powertools.utilities.JsonConfig; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; + /** * Aspect that handles the {@link Idempotent} annotation. * It uses the {@link IdempotencyHandler} to actually do the job. 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 100% 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 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 96% 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 ac5044972..bafbcbd42 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,12 +14,20 @@ package software.amazon.lambda.powertools.idempotency.persistence; -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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.utilities.JsonConfig; + import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -33,16 +41,8 @@ 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 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.exceptions.IdempotencyKeyException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; -import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; -import software.amazon.lambda.powertools.utilities.JsonConfig; + +import static software.amazon.lambda.powertools.common.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; /** * Persistence layer that will store the idempotency result. @@ -55,7 +55,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; @@ -73,7 +73,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; } @@ -339,7 +339,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"); } } @@ -402,4 +402,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() ? false : 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 99% 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..2d56fe349 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. 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/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/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/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 99% 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 c72593b66..e5f45aef7 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 @@ -14,23 +14,9 @@ package software.amazon.lambda.powertools.idempotency.internal; -import static java.time.temporal.ChronoUnit.SECONDS; -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.doReturn; -import static org.mockito.Mockito.doThrow; -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.junitpioneer.jupiter.SetEnvironmentVariable; @@ -57,6 +43,21 @@ import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; +import java.time.Instant; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import static java.time.temporal.ChronoUnit.SECONDS; +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.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + public class IdempotencyAspectTest { @Mock 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 100% 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..053b56430 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 @@ -14,10 +14,10 @@ package software.amazon.lambda.powertools.idempotency.internal.cache; -import static org.assertj.core.api.Assertions.assertThat; - import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + public class LRUCacheTest { @Test 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 99% 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..41d7e2957 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 @@ -14,18 +14,11 @@ package software.amazon.lambda.powertools.idempotency.persistence; -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 software.amazon.lambda.powertools.idempotency.IdempotencyConfig; @@ -37,6 +30,14 @@ import software.amazon.lambda.powertools.idempotency.model.Product; import software.amazon.lambda.powertools.utilities.JsonConfig; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.OptionalInt; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + public class BasePersistenceStoreTest { private DataRecord dr; 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-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml new file mode 100644 index 000000000..a8f69c468 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -0,0 +1,126 @@ +<?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.0.0-SNAPSHOT</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> + + <!-- Test dependencies --> + <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> + <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>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> \ No newline at end of file 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 98% 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 054f61ef3..0e20e396f 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,8 @@ * */ -package software.amazon.lambda.powertools.idempotency.persistence; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; -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; - -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.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; @@ -39,11 +28,25 @@ 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.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.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> @@ -310,7 +313,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/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java similarity index 98% rename from powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java index 66ddb53ac..30b4976d7 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.idempotency; +package software.amazon.lambda.powertools.idempotency.dynamodb; import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; 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.dynamodb/IdempotencyTest.java similarity index 93% 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.dynamodb/IdempotencyTest.java index c94fec3db..be915b610 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.dynamodb/IdempotencyTest.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.idempotency; +package software.amazon.lambda.powertools.idempotency.dynamodb; import static org.assertj.core.api.Assertions.assertThat; @@ -25,7 +25,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyFunction; +import software.amazon.lambda.powertools.idempotency.dynamodb.handlers.IdempotencyFunction; public class IdempotencyTest extends DynamoDBConfig { 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.dynamodb/handlers/IdempotencyFunction.java similarity index 93% 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.dynamodb/handlers/IdempotencyFunction.java index 43e191fc2..1296a75c7 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.dynamodb/handlers/IdempotencyFunction.java @@ -12,12 +12,21 @@ * */ -package software.amazon.lambda.powertools.idempotency.handlers; +package software.amazon.lambda.powertools.idempotency.dynamodb.handlers; 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.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.dynamodb.DynamoDBPersistenceStore; +import software.amazon.lambda.powertools.utilities.JsonConfig; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -25,17 +34,9 @@ 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.DynamoDBPersistenceStore; -import software.amazon.lambda.powertools.utilities.JsonConfig; public class IdempotencyFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private static final Logger LOG = LoggerFactory.getLogger(IdempotencyFunction.class); + private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); public boolean handlerExecuted = false; 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 99% 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..cc682a81f 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,16 +12,8 @@ * */ -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; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -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; @@ -39,10 +31,20 @@ 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.dynamodb.DynamoDBConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * These test are using DynamoDBLocal and sqlite, see https://nickolasfisher.com/blog/Configuring-an-In-Memory-DynamoDB-instance-with-Java-for-Integration-Testing 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/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-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 3a3018c7c..34b1238f6 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -21,7 +21,6 @@ <artifactId>powertools-parameters</artifactId> <version>${project.version}</version> </dependency> - <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>appconfigdata</artifactId> @@ -36,6 +35,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index ad32bdff8..2ec6ad27c 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -36,6 +36,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 6c4501ca3..3275d0ee0 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -36,6 +36,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 80276189d..65332c9ef 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -36,6 +36,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index a5fc4a890..0de38c1c1 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -33,6 +33,11 @@ </description> <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 747752130..ee39b5d0f 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -27,6 +27,10 @@ <Class name="software.amazon.lambda.powertools.parameters.ssm.SSMProviderBuilder"/> <Field name="transformationManager"/> </And> + <And> + <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"/> @@ -106,6 +110,10 @@ <Class name="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"/> <Field name="throwableConverter"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.parameters.transform.TransformationManager"/> + <Field name="transformer"/> + </And> <And> <Class name="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"/> <Field name="throwableConverter"/> @@ -127,7 +135,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> From 81514216a988904867d0c6689d6dceaf1748874b Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:10:09 +0100 Subject: [PATCH 0552/1008] Update README.md (#1560) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa4b872b7..83b026d15 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **This is pre-release code for Powertools for AWS Lambda (Java) V2! Please check out the `main` branch for the stable release** -![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) [![V2 Build Status](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/pr_build.yml/badge.svg?branch=v2)](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/pr_build.yml) **MAVEN DEPLOY NOT DONE** [![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-java/branch/v2/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-java/tree/v2) Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. From 982f4e0c3bc5aaadcd09b138a9bd61603986f94f Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:27:37 +0100 Subject: [PATCH 0553/1008] chore: remove unecessary creds acquisition (#1572) --- .github/workflows/pr_build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 7a02b08d4..54c2599c3 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -79,12 +79,6 @@ jobs: if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 working-directory: examples/powertools-examples-core/kotlin run: ./gradlew build - - name: Setup AWS credentials - if: ${{ matrix.java == '11' }} - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} - aws-region: ${{ env.AWS_REGION }} - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once From d49d89b6eaa8fd39054c9dc102e6974792fa5c68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:35:21 +0100 Subject: [PATCH 0554/1008] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.10 to 0.8.11 (#1509) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.10 to 0.8.11. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.10...v0.8.11) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c30dc260..df1f4e16c 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ <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.6.0</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> From 69c262bd41953c95dd53eaf4d62982c6ef59392e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:39:04 +0100 Subject: [PATCH 0555/1008] build(deps): bump com.amazonaws:aws-lambda-java-serialization (#1573) Bumps [com.amazonaws:aws-lambda-java-serialization](https://github.com/aws/aws-lambda-java-libs) from 1.1.2 to 1.1.5. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-serialization dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index df1f4e16c..9f6bcf875 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <lambda.serial.version>1.1.2</lambda.serial.version> + <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> From 1813e5fe73f3c1bf82ef062e1e5dedd361a651d2 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:32:26 +0100 Subject: [PATCH 0556/1008] chore(v2): Merge down from main (#1574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: HelloWorldStreamFunction in examples fails with sam (#1532) * Setting up Kotlin environment. Converting test to Kotlin. * Deploying via SAM successfully. * Added Kotlin example. * Removing unused Gradle build file. * Adding SAM template so can be used as an existing project and Java target compatibility * Adding SAM template so can be used as an existing project * Updating guidance to use SAM for build and deploy * Restructuring separate Java and Kotlin examples. * Updating core examples readme to represent new structure for Java and Kotlin examples. * Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive * Updating to fix trailing \n * Updating guidance to be more specific for examples * Adopting new mechanism for specifying jvm target. * accommodating new project structure * Fixing link typo after refactoring * Setting up Kotlin environment. Converting test to Kotlin. * Deploying via SAM successfully. * Added Kotlin example. * Removing unused Gradle build file. * Adding SAM template so can be used as an existing project and Java target compatibility * Adding SAM template so can be used as an existing project * Updating guidance to use SAM for build and deploy * Restructuring separate Java and Kotlin examples. * Updating core examples readme to represent new structure for Java and Kotlin examples. * Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive * Updating to fix trailing \n * Updating guidance to be more specific for examples * Adopting new mechanism for specifying jvm target. * accommodating new project structure * Fixing link typo after refactoring * Flattening structure back to original to make merging easier for v2 * Adding build for Kotlin Gradle * Adding build for Kotlin Gradle - Restructuring Java examples to v1 approach * Correcting paths * Adding SNAPSHOT support and local capability for Maven. Testing using Java 1.8 * Reviewed and updated against PR comments. * Un-commenting examples * Adding validation step for IaC SAM * Adding Terraform for Java projects IaC validator and linter * Adding additional projects for SAM validation and matrix approach * Refactoring stream function to process input logging example with a Lambda Function URL instead of APIGW. * Demonstrating Java streaming response * Refactoring stream function to process input logging example to return * Update CONTRIBUTING.md * fix: get trace id from system property when env var is not set (#1503) * fix: check if XRAY Trace ID is present in System property * chore: remove erroneous extra char in tests * fix #1500 (#1506) * feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) * chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) * feat: ALC (#1514) * handle AWS_LAMBDA_LOG configuration * ALC documentation + code review * update doc * chore:Prep release 1.18.0 (#1515) * chore:prep release 1.18.0 * update version * update version in kotlin example * maven local repo in gradle example * update changelog --------- Co-authored-by: scottgerring <scottgerring@users.noreply.github.com> * chore: update version to next snapshot: 1-19.0-SNAPSHOT (#1516) * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * building only for LTS * Add some more margin to the test pause (#1518) * test: e2e tests with java 21 (#1517) * e2e tests with java 21 * Run Java21 tests using the Java17 compiler * Run all of the E2E tests in parallel, not just the first 3 * Try again * . * Let's try again * Add some comment on Java21 to the repo * Add caveat about lambda runtimes * Clean up wording a little --------- Co-authored-by: Scott Gerring <gerrings@amazon.com> * update doc for ALC (#1520) * chore: Testing java21 aspectj pre-release (#1519) * e2e tests with java 21 * use aspectj 1.9.21-SNAPSHOT * Fix log4j2.xml missing in logging test for java21 * rollback double runtime * remove comment * keep aspectj 1.9.7 in parent for java8 compatibility * use M1 instead of snapshot * update documentation for aspectj * update documentation for aspectj --------- Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> * chore: Remove build cruft * Adding context for using RequestStreamHandler * removing pr_lint * Update examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java Clarify usage of RequestStreamHandler. Co-authored-by: Alexey Soshin <alexey.soshin@gmail.com> --------- Co-authored-by: Jason Harris <harrzjas@amazon.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> Co-authored-by: Jason Harris <harrzjas@amazon.co.uk> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> Co-authored-by: Michele Ricciardi <mriccia@amazon.com> Co-authored-by: Alexey Soshin <alexey.soshin@gmail.com> Co-authored-by: jdoherty <jdoherty07@gmail.com> Co-authored-by: Scott Gerring <gerrings@amazon.com> Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> * deps: bump aspectj to 1.9.21 for jdk21 (#1536) * chore: SAM and Terraform IaC extracted from pr_build and simplified approach. (#1533) * SAM and Terraform IaC extracted from pr_build and simplified approach. * Update .github/workflows/pr_iac_lint.yml Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --------- Co-authored-by: Jason Harris <harrzjas@amazon.co.uk> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * chore: Remove empty CDK test (#1542) * Remove CDK test * Build core utilities so that sam validator can find them * Remove CDK test * Ok just build it all * Remove the plan ... * Remove terraform plan from iac lint --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * dependabot on v2 branch (#1548) * chore: remove unecessary creds acquisition (#1572) * Merge from main --------- Co-authored-by: Jason Harris <jiharris90@gmail.com> Co-authored-by: Jason Harris <harrzjas@amazon.com> Co-authored-by: Jason Harris <harrzjas@amazon.co.uk> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> Co-authored-by: Michele Ricciardi <mriccia@amazon.com> Co-authored-by: Alexey Soshin <alexey.soshin@gmail.com> Co-authored-by: jdoherty <jdoherty07@gmail.com> Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> --- .github/dependabot.yml | 9 ++++ .github/workflows/pr_build.yml | 27 ---------- .github/workflows/pr_iac_lint.yml | 52 +++++++++++++++++++ docs/index.md | 3 -- .../src/main/java/helloworld/AppStream.java | 29 +++++++++-- .../infra/src/test/java/cdk/CdkStackTest.java | 48 ----------------- .../src/main/java/helloworld/AppStream.java | 29 +++++++++-- powertools-e2e-tests/handlers/pom.xml | 2 +- 8 files changed, 112 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/pr_iac_lint.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1454df79c..28adf2bc1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,3 +11,12 @@ updates: # Ignore Mockito 5.X.X as it does not support Java 8 - dependency-name: "org.mockito:mockito-*" update-types: ["version-update:semver-major"] + + - package-ecosystem: "maven" + directory: "/" + target-branch: "v2" + schedule: + interval: "weekly" + labels: + - "maven" + - "dependencies" diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 5373f4b4c..4fa8dc7c0 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -80,33 +80,6 @@ jobs: if: ${{ matrix.java != '8' }} working-directory: examples/powertools-examples-core-utilities/kotlin run: ./gradlew build - - name: Setup Terraform - if: ${{ matrix.java == '11' }} - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - - name: Setup AWS credentials - if: ${{ matrix.java == '11' }} - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} - aws-region: ${{ env.AWS_REGION }} - - name: Terraform validate - working-directory: examples/powertools-examples-core-utilities/terraform - if: ${{ matrix.java == '11' }} - run: | - terraform -version - terraform init -backend=false - terraform validate - terraform plan - - name: Setup Terraform lint - if: ${{ matrix.java == '11' }} - uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - - name: Terraform lint - working-directory: examples/powertools-examples-core-utilities/terraform - if: ${{ matrix.java == '11' }} - run: | - tflint --version - tflint --init - tflint -f compact - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml new file mode 100644 index 000000000..c6e17ab1c --- /dev/null +++ b/.github/workflows/pr_iac_lint.yml @@ -0,0 +1,52 @@ +name: Validate IaC + +on: + push: + branches: + - main + - v2 + pull_request: + branches: + - main + - v2 + paths: + - 'examples/**' +jobs: + linter: + runs-on: ubuntu-latest + strategy: + matrix: + project: ["sam", "gradle", "kotlin"] + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java JDK + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: 11 + - name: Build Project + working-directory: . + run: | + mvn install -DskipTests + - name: Run SAM validator to check syntax of IaC templates - Java + working-directory: examples/powertools-examples-core/${{ matrix.project }} + run: | + sam build + sam validate --lint + - name: Setup Terraform + uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 + - name: Run Terraform validator to check syntax of IaC templates and produce a plan of changes + working-directory: examples/powertools-examples-core/terraform + run: | + mvn install + terraform -version + terraform init -backend=false + terraform validate + - name: Setup Terraform lint + uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 + - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. + working-directory: examples/powertools-examples-core/terraform + run: | + tflint --version + tflint --init + tflint -f compact \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 884e02476..06c9beb6d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -306,9 +306,6 @@ Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj | `11-17` | `1.9.20.1` | | `21` | `1.9.21` | -_Note: 1.9.21 is not yet available and Java 21 not yet officially supported by aspectj, but you can already use the `1.9.21.M1`_ - - ## Environment variables !!! info 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 index 401ef8c48..94806cc38 100644 --- 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 @@ -17,22 +17,43 @@ 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.util.Map; +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.Metrics; +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) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); + // 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)))) { - System.out.println(map.size()); + 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/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core-utilities/cdk/infra/src/test/java/cdk/CdkStackTest.java index 29cb15545..e69de29bb 100644 --- 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 @@ -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-utilities/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/gradle/src/main/java/helloworld/AppStream.java index 401ef8c48..94806cc38 100644 --- 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 @@ -17,22 +17,43 @@ 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.util.Map; +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.Metrics; +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) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - Map map = mapper.readValue(input, Map.class); + // 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)))) { - System.out.println(map.size()); + 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/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 412593da9..ef5be4df4 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -213,7 +213,7 @@ <jdk>[21,)</jdk> </activation> <properties> - <aspectj.version>1.9.21.M1</aspectj.version> + <aspectj.version>1.9.21</aspectj.version> </properties> </profile> </profiles> From c6bdf96dff840f366103edb245179b233d24e320 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:39:47 +0100 Subject: [PATCH 0557/1008] fix PR lint (#1575) --- .github/workflows/pr_iac_lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml index c6e17ab1c..39c2bc5df 100644 --- a/.github/workflows/pr_iac_lint.yml +++ b/.github/workflows/pr_iac_lint.yml @@ -29,14 +29,14 @@ jobs: run: | mvn install -DskipTests - name: Run SAM validator to check syntax of IaC templates - Java - working-directory: examples/powertools-examples-core/${{ matrix.project }} + working-directory: examples/powertools-examples-core-utilities//${{ matrix.project }} run: | sam build sam validate --lint - name: Setup Terraform uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - name: Run Terraform validator to check syntax of IaC templates and produce a plan of changes - working-directory: examples/powertools-examples-core/terraform + working-directory: examples/powertools-examples-core-utilities/terraform run: | mvn install terraform -version From e62e2cd700eb4c0ea824acef1c3533a4f570393d Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:46:32 +0100 Subject: [PATCH 0558/1008] chore(v2): Fix IaC lint (#1576) * fix PR lint * And another one --- .github/workflows/pr_iac_lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml index 39c2bc5df..09ba5f02b 100644 --- a/.github/workflows/pr_iac_lint.yml +++ b/.github/workflows/pr_iac_lint.yml @@ -45,7 +45,7 @@ jobs: - name: Setup Terraform lint uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. - working-directory: examples/powertools-examples-core/terraform + working-directory: examples/powertools-examples-core-utilities/terraform run: | tflint --version tflint --init From c389372a322c84fa86e7ab2b3170e190ea4d58ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:08:51 +0100 Subject: [PATCH 0559/1008] build(deps): bump org.apache.maven.plugins:maven-artifact-plugin (#1567) Bumps [org.apache.maven.plugins:maven-artifact-plugin](https://github.com/apache/maven-artifact-plugin) from 3.4.1 to 3.5.0. - [Commits](https://github.com/apache/maven-artifact-plugin/compare/maven-artifact-plugin-3.4.1...maven-artifact-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-artifact-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c7ca43d3..3dee839dc 100644 --- a/pom.xml +++ b/pom.xml @@ -384,7 +384,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-artifact-plugin</artifactId> - <version>3.4.1</version> + <version>3.5.0</version> <configuration> <reproducible>true</reproducible> </configuration> From c26ada613651e6d25fe13d59d62d70870f9ab47f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:09:15 +0100 Subject: [PATCH 0560/1008] build(deps): bump aws.sdk.version from 2.21.0 to 2.24.5 (#1566) Bumps `aws.sdk.version` from 2.21.0 to 2.24.5. Updates `software.amazon.awssdk:bom` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:http-client-spi` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:url-connection-client` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:dynamodb` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:s3` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:lambda` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:kinesis` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:cloudwatch` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:xray` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:sqs` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:cloudformation` from 2.21.0 to 2.24.5 Updates `software.amazon.awssdk:sts` from 2.21.0 to 2.24.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8851daa6d..795834a8f 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.21.0</aws.sdk.version> + <aws.sdk.version>2.24.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 3dee839dc..f7a794707 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.16.0</jackson.version> - <aws.sdk.version>2.21.0</aws.sdk.version> + <aws.sdk.version>2.24.5</aws.sdk.version> <aws.xray.recorder.version>2.15.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 0c23a18b9f637adb2a56a0a690f008f5994fe1b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:09:29 +0100 Subject: [PATCH 0561/1008] build(deps): bump org.assertj:assertj-core from 3.24.2 to 3.25.3 (#1565) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.24.2 to 3.25.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.24.2...assertj-build-3.25.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f7a794707..4fa6e0039 100644 --- a/pom.xml +++ b/pom.xml @@ -291,7 +291,7 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.24.2</version> + <version>3.25.3</version> <scope>test</scope> <exclusions> <exclusion> From 1ec0d171ba4e90fd921ec79da44b3b1da53f7bc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:10:24 +0100 Subject: [PATCH 0562/1008] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin (#1564) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.6.0 to 3.6.3. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.0...maven-javadoc-plugin-3.6.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4fa6e0039..0b5cd16cb 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> - <maven-javadoc-plugin.version>3.6.0</maven-javadoc-plugin.version> + <maven-javadoc-plugin.version>3.6.3</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> From 8a8bd136064e6223be2c1d5656b9987815259b35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:43:31 +0100 Subject: [PATCH 0563/1008] build(deps): bump org.apache.maven.plugins:maven-artifact-plugin (#1485) Bumps [org.apache.maven.plugins:maven-artifact-plugin](https://github.com/apache/maven-artifact-plugin) from 3.4.1 to 3.5.0. - [Commits](https://github.com/apache/maven-artifact-plugin/compare/maven-artifact-plugin-3.4.1...maven-artifact-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-artifact-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f6bcf875..0d0710634 100644 --- a/pom.xml +++ b/pom.xml @@ -365,7 +365,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-artifact-plugin</artifactId> - <version>3.4.1</version> + <version>3.5.0</version> <configuration> <reproducible>true</reproducible> </configuration> From 4f6889133fc4d570cb6f62d74ca54ca484838e00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:43:55 +0100 Subject: [PATCH 0564/1008] build(deps): bump log4j.version from 2.20.0 to 2.22.1 (#1547) Bumps `log4j.version` from 2.20.0 to 2.22.1. Updates `org.apache.logging.log4j:log4j-core` from 2.20.0 to 2.22.1 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.20.0 to 2.22.1 Updates `org.apache.logging.log4j:log4j-api` from 2.20.0 to 2.22.1 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.20.0 to 2.22.1 Updates `org.apache.logging.log4j:log4j-jcl` from 2.20.0 to 2.22.1 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/app/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-core/serverless/pom.xml | 2 +- examples/powertools-examples-core/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 54d6b50ef..c843d5adc 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -10,7 +10,7 @@ <name>AWS Lambda Powertools for Java library Examples - CloudFormation</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index 9d9435ac3..7f2328227 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -10,7 +10,7 @@ <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 16f82ebd6..9451b0628 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -10,7 +10,7 @@ <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index f8d9ab684..8e6b93528 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -10,7 +10,7 @@ <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index eef73207e..6551aaae1 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -10,7 +10,7 @@ <name>Powertools for AWS Lambda (Java) library Examples - Core</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index bf4c041bf..9e705f4b0 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -23,7 +23,7 @@ <name>Powertools for AWS Lambda (Java) library Examples - Idempotency</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index a6737bfed..bb11e93fe 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -8,7 +8,7 @@ <name>Powertools for AWS Lambda (Java) library Examples - SQS</name> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> diff --git a/pom.xml b/pom.xml index 0d0710634..c56d9db10 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.22.1</log4j.version> <jackson.version>2.15.3</jackson.version> <aws.sdk.version>2.21.0</aws.sdk.version> <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> From 05853833393bc35af3d21d5d3616e8661c0b75e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:31:17 +0100 Subject: [PATCH 0565/1008] build(deps-dev): bump org.yaml:snakeyaml from 2.1 to 2.2 (#1400) Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 2.1 to 2.2. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.2..snakeyaml-2.1) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8a0b65f32..540490f20 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -159,7 +159,7 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>2.1</version> + <version>2.2</version> <scope>test</scope> </dependency> <dependency> From 5074d2e344d8ad8a2068f58f88d4a2650c9c9788 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:15:31 +0100 Subject: [PATCH 0566/1008] build(deps): bump aws.xray.recorder.version from 2.15.0 to 2.15.1 (#1577) Bumps `aws.xray.recorder.version` from 2.15.0 to 2.15.1. Updates `com.amazonaws:aws-xray-recorder-sdk-core` from 2.15.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.0...v2.15.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core` from 2.15.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.0...v2.15.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2` from 2.15.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.0...v2.15.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.15.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.0...v2.15.1) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b5cd16cb..7b126a426 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.16.0</jackson.version> <aws.sdk.version>2.24.5</aws.sdk.version> - <aws.xray.recorder.version>2.15.0</aws.xray.recorder.version> + <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> From 91993f4dac70c44c20b6bb8c2f1bbc02742829b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:15:46 +0100 Subject: [PATCH 0567/1008] build(deps): bump aws.sdk.version from 2.21.1 to 2.24.10 (#1580) Bumps `aws.sdk.version` from 2.21.1 to 2.24.10. Updates `software.amazon.awssdk:url-connection-client` from 2.21.1 to 2.24.10 Updates `software.amazon.awssdk:sdk-core` from 2.21.1 to 2.24.10 Updates `software.amazon.awssdk:kinesis` from 2.21.1 to 2.24.10 Updates `software.amazon.awssdk:sqs` from 2.21.1 to 2.24.10 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.21.1 to 2.24.10 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 947f146b3..159ff52cd 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.21.1</sdk.version> + <sdk.version>2.24.10</sdk.version> </properties> <dependencies> From 9020f46af33a192d03f6bdd2b4848f0f262a917f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:44:28 +0100 Subject: [PATCH 0568/1008] build(deps): bump org.apache.maven.plugins:maven-shade-plugin (#1582) Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.0...maven-shade-plugin-3.5.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/app/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-core/serverless/pom.xml | 2 +- examples/powertools-examples-core/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index c8f829997..d3539d919 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -97,7 +97,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c843d5adc..e8526e14d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -122,7 +122,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index 7f2328227..561aae686 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -90,7 +90,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 9451b0628..2e95644b6 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -89,7 +89,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 8e6b93528..59820ffec 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -90,7 +90,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 6551aaae1..7fe562072 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -90,7 +90,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 9e705f4b0..c841c0f38 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -135,7 +135,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index bb11e93fe..0e1382faa 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -95,7 +95,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.5.2</version> <executions> <execution> <phase>package</phase> From d60eb14173b50b43889f26ce41153b50fe064491 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:44:44 +0100 Subject: [PATCH 0569/1008] build(deps): bump aws.xray.recorder.version from 2.14.0 to 2.15.1 (#1583) Bumps `aws.xray.recorder.version` from 2.14.0 to 2.15.1. Updates `com.amazonaws:aws-xray-recorder-sdk-core` from 2.14.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.14.0...v2.15.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core` from 2.14.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.14.0...v2.15.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2` from 2.14.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.14.0...v2.15.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.14.0 to 2.15.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.14.0...v2.15.1) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c56d9db10..3ed285f61 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ <log4j.version>2.22.1</log4j.version> <jackson.version>2.15.3</jackson.version> <aws.sdk.version>2.21.0</aws.sdk.version> - <aws.xray.recorder.version>2.14.0</aws.xray.recorder.version> + <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> From c7980592ae75b44649ef015ccc275e5f3f5a24ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:48:34 +0100 Subject: [PATCH 0570/1008] build(deps): bump commons-io:commons-io from 2.13.0 to 2.15.1 (#1584) Bumps commons-io:commons-io from 2.13.0 to 2.15.1. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 540490f20..e33c592c4 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -103,7 +103,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.13.0</version> + <version>2.15.1</version> </dependency> <dependency> From 79378a10e5bdb2522bea1bea7e234da5b6795991 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:52:18 +0100 Subject: [PATCH 0571/1008] build(deps): bump aws.sdk.version from 2.21.0 to 2.24.10 (#1581) Bumps `aws.sdk.version` from 2.21.0 to 2.24.10. Updates `software.amazon.awssdk:bom` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:http-client-spi` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:url-connection-client` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:sqs` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:s3` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:dynamodb` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:lambda` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:kinesis` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:cloudwatch` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:xray` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:cloudformation` from 2.21.0 to 2.24.10 Updates `software.amazon.awssdk:sts` from 2.21.0 to 2.24.10 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index e8526e14d..3b2cf6df0 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.21.0</aws.sdk.version> + <aws.sdk.version>2.24.10</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0e1382faa..d83a900c0 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.21.1</version> + <version>2.24.10</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index 3ed285f61..1337d2721 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <log4j.version>2.22.1</log4j.version> <jackson.version>2.15.3</jackson.version> - <aws.sdk.version>2.21.0</aws.sdk.version> + <aws.sdk.version>2.24.10</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 55bd043fa6487d5fc34e51b6dd27c347786bf39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:34:41 +0100 Subject: [PATCH 0572/1008] Update pr_artifacts_size.yml adding permissions --- .github/workflows/pr_artifacts_size.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml index f37f83a8d..36bf301df 100644 --- a/.github/workflows/pr_artifacts_size.yml +++ b/.github/workflows/pr_artifacts_size.yml @@ -26,6 +26,8 @@ on: jobs: codecheck: runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java JDK 11 @@ -61,4 +63,4 @@ jobs: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body-path: 'report.md' - edit-mode: replace \ No newline at end of file + edit-mode: replace From cdad9904bef63e7cf04da7da3c1cecc772249cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:40:35 +0100 Subject: [PATCH 0573/1008] Update pr_artifacts_size.yml --- .github/workflows/pr_artifacts_size.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml index 36bf301df..ab9ca9859 100644 --- a/.github/workflows/pr_artifacts_size.yml +++ b/.github/workflows/pr_artifacts_size.yml @@ -28,6 +28,7 @@ jobs: runs-on: ubuntu-latest permissions: pull-requests: write + issues: read steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java JDK 11 From 2289d974d2901b144f66f66f9faabb09aa1933a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:50:54 +0100 Subject: [PATCH 0574/1008] build(deps): bump io.burt:jmespath-jackson from 0.5.1 to 0.6.0 (#1587) Bumps [io.burt:jmespath-jackson](https://github.com/burtcorp/jmespath-java) from 0.5.1 to 0.6.0. - [Release notes](https://github.com/burtcorp/jmespath-java/releases) - [Commits](https://github.com/burtcorp/jmespath-java/compare/jmespath-0.5.1...jmespath-0.6.0) --- updated-dependencies: - dependency-name: io.burt:jmespath-jackson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1337d2721..2ad359a3a 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ <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> + <jmespath.version>0.6.0</jmespath.version> </properties> <distributionManagement> From 98900325c67d27708979fb09d8a0fbb1df1063e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:54:29 +0100 Subject: [PATCH 0575/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1586) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.100.0 to 2.130.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.100.0...v2.130.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index e2a1d6b9a..021aa98a4 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>1.19.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.100.0</cdk.version> + <cdk.version>2.130.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e33c592c4..450d1f5c1 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <constructs.version>10.3.0</constructs.version> - <cdk.version>2.109.0</cdk.version> + <cdk.version>2.130.0</cdk.version> </properties> <dependencies> From 716cfc899e4ad099e61eefec4a1200245074ee61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:23:29 +0100 Subject: [PATCH 0576/1008] build(deps): bump org.codehaus.mojo:exec-maven-plugin (#1585) Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/exec-maven-plugin-3.1.0...3.2.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 021aa98a4..05e643c4d 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -25,7 +25,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.1.0</version> + <version>3.2.0</version> <configuration> <mainClass>cdk.CdkApp</mainClass> </configuration> From 5bf9b3ddec0209e747961a33da2e24d9178a2670 Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Wed, 28 Feb 2024 12:59:20 +0100 Subject: [PATCH 0577/1008] chore(v2): e2e tests (#1571) * Chore: fix e2e tests and build config * Use the right aspect library --- examples/powertools-examples-idempotency/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 4 ++-- .../software/amazon/lambda/powertools/e2e/Function.java | 2 +- .../handlers/largemessage_idempotent/pom.xml | 4 ++-- .../software/amazon/lambda/powertools/e2e/Function.java | 2 +- powertools-e2e-tests/handlers/pom.xml | 8 +++++--- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 39d6a8172..9a9351ea5 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -95,7 +95,7 @@ </aspectLibrary> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency-dynamodb</artifactId> + <artifactId>powertools-idempotency-core</artifactId> </aspectLibrary> </aspectLibraries> </configuration> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index da2bbfb80..e3a67a5b5 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -15,7 +15,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> @@ -43,7 +43,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/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..16109778d 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; diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 8cb2cb52c..b57063346 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -15,7 +15,7 @@ <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency</artifactId> + <artifactId>powertools-idempotency-dynamodb</artifactId> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> @@ -47,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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ef5be4df4..1bf26f871 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -32,10 +32,7 @@ <module>logging</module> <module>tracing</module> <module>metrics</module> - <module>batch</module> <module>idempotency</module> - <module>largemessage</module> - <module>largemessage_idempotent</module> <module>parameters</module> <module>validation-alb-event</module> <module>validation-apigw-event</module> @@ -75,6 +72,11 @@ <artifactId>powertools-idempotency</artifactId> <version>${lambda.powertools.version}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-idempotency-dynamodb</artifactId> + <version>${lambda.powertools.version}</version> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parameters-appconfig</artifactId> From 7dff30c1afd1bf710ac0984aecc6efc973ef31cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:29:34 +0100 Subject: [PATCH 0578/1008] feat: advanced logging (#1539) * improve logging * adding arguments to log events * !update ObjectMapper configuration * remove LoggingUtils appendKey, use standard MDC * examples in the end * clean sonar warnings * clean spotbugs issues * code review comments * update documentation * remove LoggingUtils --------- Co-authored-by: Scott Gerring <gerrings@amazon.com> --- docs/core/logging.md | 359 ++++------- .../cdk/app/src/main/java/helloworld/App.java | 4 +- .../gradle/src/main/java/helloworld/App.java | 7 +- .../kotlin/src/main/kotlin/helloworld/App.kt | 11 +- .../sam/src/main/java/helloworld/App.java | 7 +- .../src/main/java/helloworld/App.java | 12 +- .../src/main/java/helloworld/App.java | 16 +- pom.xml | 2 +- .../lambda/powertools/e2e/Function.java | 4 +- .../persistence/BasePersistenceStoreTest.java | 28 +- .../handlers/IdempotencyFunction.java | 35 +- .../json/resolver/PowertoolsResolver.java | 115 ++-- .../src/main/resources/LambdaJsonLayout.json | 3 +- ...a => PowertoolsResolverArgumentsTest.java} | 35 +- .../json/resolver/PowertoolsResolverTest.java | 8 + ...nMessage.java => PowertoolsArguments.java} | 31 +- .../handler/PowertoolsLogEnabled.java | 6 +- .../powertools/logging/logback/JsonUtils.java | 139 ++++ .../logging/logback/LambdaEcsEncoder.java | 165 +++-- .../logging/logback/LambdaJsonEncoder.java | 134 ++-- .../logging/logback/internal/JsonUtils.java | 130 ---- .../logback/internal/LambdaEcsSerializer.java | 187 ------ .../internal/LambdaJsonSerializer.java | 150 ----- .../internal/LambdaJsonEncoderTest.java | 213 ++++++- ...nMessage.java => PowertoolsArguments.java} | 28 +- .../handler/PowertoolsLogEnabled.java | 6 +- .../internal/handler/PowertoolsLogEvent.java | 28 + .../handler}/PowertoolsLogEventDisabled.java | 4 +- .../handler/PowertoolsLogEventForStream.java | 34 + .../handler/PowertoolsLogResponse.java | 28 + .../PowertoolsLogResponseForStream.java | 35 + .../lambda/powertools/logging/Logging.java | 3 +- .../powertools/logging/LoggingUtils.java | 123 ---- .../logging/argument/ArrayArgument.java | 43 ++ .../logging/argument/JsonArgument.java | 42 ++ .../logging/argument/KeyValueArgument.java | 49 ++ .../logging/argument/MapArgument.java | 48 ++ .../logging/argument/StructuredArgument.java | 45 ++ .../logging/argument/StructuredArguments.java | 129 ++++ .../logging/internal/JsonSerializer.java | 599 ++++++++++++++++++ .../logging/internal/LambdaLoggingAspect.java | 57 +- .../test/java/org/slf4j/test/TestLogger.java | 12 + .../powertools/logging/LoggingUtilsTest.java | 114 ---- .../argument/StructuredArgumentsTest.java | 105 +++ .../handlers/PowertoolsLogClearState.java | 4 +- .../handlers/PowertoolsLogEnabled.java | 6 +- .../logging/handlers/PowertoolsLogEvent.java | 8 + .../handlers/PowertoolsLogEventEnvVar.java | 36 ++ .../handlers/PowertoolsLogEventForStream.java | 8 + .../handlers/PowertoolsLogResponse.java | 7 + .../PowertoolsLogResponseForStream.java | 8 + .../logging/internal/JsonSerializerTest.java | 149 +++++ .../internal/LambdaLoggingAspectTest.java | 122 ++-- .../powertools/logging/model/Basket.java | 67 ++ .../powertools/logging/model/Product.java | 84 +++ .../powertools/utilities/JsonConfig.java | 26 +- .../utilities/EventDeserializerTest.java | 17 - spotbugs-exclude.xml | 11 +- 58 files changed, 2538 insertions(+), 1348 deletions(-) rename powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/{PowertoolsMessageResolverTest.java => PowertoolsResolverArgumentsTest.java} (70%) rename powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/{PowertoolsJsonMessage.java => PowertoolsArguments.java} (62%) create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java delete mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java delete mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaEcsSerializer.java delete mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java rename powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/{PowertoolsJsonMessage.java => PowertoolsArguments.java} (62%) create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.java rename powertools-logging/{src/test/java/software/amazon/lambda/powertools/logging/handlers => powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler}/PowertoolsLogEventDisabled.java (90%) create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponse.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogResponseForStream.java delete mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/ArrayArgument.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/JsonArgument.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/KeyValueArgument.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/MapArgument.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArgument.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArguments.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JsonSerializer.java delete mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/argument/StructuredArgumentsTest.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventEnvVar.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/JsonSerializerTest.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Basket.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/model/Product.java diff --git a/docs/core/logging.md b/docs/core/logging.md index f0fba760a..5306c55df 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -338,73 +338,6 @@ Your logs will always include the following keys in your structured logging: | **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"));`) | -### Log messages as JSON -By default, `message` is logged as a `String` (e.g `"message": "The message"`). When logging JSON content, -you may want to avoid the escaped String (`"message:"{\"key\":\"value\"}"`) for better readability. -You can use `LoggingUtils.logMessagesAsJson(true)` to enable this programmatically. - -=== "PaymentFunction.java" - - ```java hl_lines="14 15 17-20" - import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; - import software.amazon.lambda.powertools.logging.LoggingUtils; - import software.amazon.lambda.powertools.utilities.JsonConfig; - // ... 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) { - Order order = extractDataFrom(input).as(Order.class); - - // logged as a String - LOGGER.debug("{}", JsonConfig.get().getObjectMapper().writeValueAsString(order)); - - // Logged as JSON - LoggingUtils.logMessagesAsJson(true); - LOGGER.debug("{}", JsonConfig.get().getObjectMapper().writeValueAsString(order)); - LoggingUtils.logMessagesAsJson(false); - - // ... - } - } - ``` - -=== "Order.java" - - ```java - public class Order { - private String id; - private Date date; - private Double amount; - } - ``` - -=== "Example CloudWatch Logs" - - ```json hl_lines="3 9-13" - { - "level": "DEBUG", - "message": "{\"id\":\"435iuh2j3hb4\", \"date\":\"2023-12-01T14:48:59\", \"amount\":435.5}", - "timestamp": "2023-12-01T14:49:19.293Z", - "service": "payment", - } - { - "level": "DEBUG", - "message": { - "id": "435iuh2j3hb4", - "date": "2023-12-01T14:48:59", - "amount":435.5 - }, - "timestamp": "2023-12-01T14:49:19.312Z", - "service": "payment", - } - ``` - -You can also achieve this more broadly for all JSON messages (see advanced configuration for [log4j](#log-messages-as-json_1) & [logback](#log-messages-as-json_2)). - ## Additional structured keys ### Logging Lambda context information @@ -463,58 +396,6 @@ including our custom [JMESPath Functions](../utilities/serialization.md#built-in "correlation_id": "correlation_id_value" } ``` - -**setCorrelationId method** - -You can also use `LoggingUtils.setCorrelationId()` method to inject it anywhere else in your code. - -=== "AppSetCorrelationId.java" - - ```java hl_lines="8" - public class AppSetCorrelationId implements RequestHandler<ScheduledEvent, String> { - - private static final Logger LOGGER = LoggerFactory.getLogger(AppSetCorrelationId.class); - - @Logging - public String handleRequest(final ScheduledEvent event, final Context context) { - // ... - LoggingUtils.setCorrelationId(event.getId()); - LOGGER.info("Scheduled Event") - // ... - } - } - ``` - -=== "Example Schedule Event" - - ```json hl_lines="2" - { - "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", - "detail-type": "Scheduled Event", - "source": "aws.events", - "account": "123456789012", - "time": "2023-12-01T14:49:19Z", - "region": "us-east-1", - "resources": [ - "arn:aws:events:us-east-1:123456789012:rule/ExampleRule" - ], - "detail": {} - } - ``` - -=== "CloudWatch Logs with correlation id" - - ```json hl_lines="6" - { - "level": "INFO", - "message": "Scheduled Event", - "timestamp": "2023-12-01T14:49:19.293Z", - "service": "payment", - "correlation_id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c" - } - ``` -???+ tip - You can retrieve correlation IDs via `LoggingUtils.getCorrelationId()` method if needed. **Known correlation IDs** @@ -563,14 +444,16 @@ we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions) #### Custom keys -???+ warning "Custom keys 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). +** Using StructuredArguments ** -To append an additional key in your logs, you can use the `LoggingUtils.appendKey()` or `LoggingUtils.appendKeys()` for multiple keys: +To append additional keys in your logs, you can use the `StructuredArguments` class: === "PaymentFunction.java" - ```java hl_lines="8 9 15 16" + ```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> { private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); @@ -578,20 +461,18 @@ To append an additional key in your logs, you can use the `LoggingUtils.appendKe @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { // ... - LoggingUtils.appendKey("orderId", order.getId()); - LOGGER.info("Collecting payment"); + LOGGER.info("Collecting payment", entry("orderId", order.getId())); // ... Map<String, String> customKeys = new HashMap<>(); customKeys.put("paymentId", payment.getId()); customKeys.put("amount", payment.getAmount); - LoggingUtils.appendKeys(customKeys); - LOGGER.info("Payment successful"); + LOGGER.info("Payment successful", entries(customKeys)); } } ``` -=== "Example CloudWatch Logs" +=== "CloudWatch Logs for PaymentFunction" ```json hl_lines="7 16-18" { @@ -615,73 +496,153 @@ To append an additional key in your logs, you can use the `LoggingUtils.appendKe } ``` -???+ tip "Additional keys are based on the MDC" - 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"}). - - `LoggingUtils.appendKey("key", "value")` is equivalent to `MDC.put("key", "value")`. +`StructuredArguments` provides several options: + - `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. -### Removing additional keys - -You can remove any additional key from entry using `LoggingUtils.removeKey()` or `LoggingUtils.removeKeys()` for multiple keys: +=== "OrderFunction.java" -=== "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.array; - ```java hl_lines="19 20" - public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + public class OrderFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); - @Logging(logResponse = true) + @Logging public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { // ... - LoggingUtils.appendKey("orderId", order.getId()); - LOGGER.info("Collecting payment"); - - // ... - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("paymentId", payment.getId()); - customKeys.put("amount", payment.getAmount); - LoggingUtils.appendKeys(customKeys); - LOGGER.info("Payment successful"); - + LOGGER.info("Processing order", entry("order", order), array("products", productList)); // ... - LoggingUtils.removeKey("orderId"); - LoggingUtils.removeKeys("paymentId", "amount"); - - return response; } } ``` -=== "Example CloudWatch Logs" - Response is logged (`logResponse=true`) without the additional keys: +=== "CloudWatch Logs for OrderFunction" - ```json - ... + ```json hl_lines="7 13" { "level": "INFO", - "message": { - "statusCode": 200, - "isBase64Encoded": false, - "body": ..., - "headers": ..., - "multiValueHeaders": ... - }, + "message": "Processing order", "service": "payment", - "timestamp": "2023-12-01T14:49:20.118Z", - "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5" + "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 "Additional keys are based on the MDC" - `LoggingUtils.removeKey("key")` is equivalent to `MDC.remove("key")`. +???+ 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. + + === "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 + } + } + ``` + +** Using MDC ** + +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: + +`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). + + +### 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 can be persisted across invocations. If you want all custom keys to be deleted, you can use +this means that custom keys, added with the MDC can be persisted across invocations. If you want all custom keys to be deleted, you can use `clearState=true` attribute on the `@Logging` annotation. === "CreditCardFunction.java" @@ -694,7 +655,7 @@ this means that custom keys can be persisted across invocations. If you want all @Logging(clearState = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { // ... - LoggingUtils.appendKey("cardNumber", card.getId()); + MDC.put("cardNumber", card.getId()); LOGGER.info("Updating card information"); // ... } @@ -727,8 +688,7 @@ this means that custom keys can be persisted across invocations. If you want all } ``` -???+ tip "Additional keys are based on the MDC" - `clearState` is based on `MDC.clear()`. State clearing is automatically done at the end of the execution of the handler if set to `true`. +`clearState` is based on `MDC.clear()`. State clearing is automatically done at the end of the execution of the handler if set to `true`. ## Logging incoming event @@ -952,22 +912,6 @@ The `JsonTemplateLayout` is automatically configured with the provided template: 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: -#### Log messages as JSON -`message` field is not handled with the standard [`MessageResolver`](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-template-resolver-message){target="_blank"} but by the `PowertoolsResolver`. -With this resolver, you can choose to log all the JSON messages as JSON and not as String. - -=== "my-custom-template.json" - - ```json - { - "message": { - "$resolver": "powertools", - "field": "message", - "asJson": true - } - } - ``` - #### 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. @@ -1015,16 +959,6 @@ Logback configuration is done in _logback.xml_ and the Powertools [`LambdaJsonEn The `LambdaJsonEncoder` can be customized in different ways: -#### Log messages as JSON - -With the following configuration, you choose to log all the JSON messages as JSON and not as String (default is `false`): - -```xml - <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> - <logMessagesAsJson>true</logMessagesAsJson> - </encoder> -``` - #### 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: @@ -1071,33 +1005,6 @@ If you need to customize format and timezone, you can change use the following: </encoder> ``` -## Override default object mapper - -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. - -=== "App.java" - - ```java hl_lines="6-10" - public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - - static { - ObjectMapper objectMapper = new ObjectMapper() - .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - LoggingUtils.setObjectMapper(objectMapper); - } - - @Logging(logEvent = true) - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - // ... - } - } - ``` - ## 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. diff --git a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java index 18eea0560..5aa268ffe 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java @@ -31,10 +31,10 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; @@ -62,7 +62,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); }); - LoggingUtils.appendKey("test", "willBeLogged"); + MDC.put("test", "willBeLogged"); APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); 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 index 36ef72ae7..b1a701b8f 100644 --- 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 @@ -14,6 +14,7 @@ package helloworld; +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; 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; @@ -31,10 +32,10 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; @@ -63,13 +64,13 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); }); - 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); 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 index 1c925d4f4..8e8857079 100644 --- 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 @@ -18,23 +18,21 @@ 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.Logger import org.slf4j.LoggerFactory +import org.slf4j.MDC 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.logging.Logging -import software.amazon.lambda.powertools.logging.LoggingUtils +import software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry import software.amazon.lambda.powertools.metrics.Metrics import software.amazon.lambda.powertools.metrics.MetricsUtils import software.amazon.lambda.powertools.tracing.CaptureMode import software.amazon.lambda.powertools.tracing.Tracing import software.amazon.lambda.powertools.tracing.TracingUtils -import java.io.BufferedReader import java.io.IOException import java.io.InputStreamReader import java.net.URL -import java.util.stream.Collectors /** * Handler for requests to Lambda function. @@ -44,7 +42,6 @@ class App : RequestHandler<APIGatewayProxyRequestEvent?, APIGatewayProxyResponse @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) @Metrics(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") MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) @@ -52,11 +49,11 @@ class App : RequestHandler<APIGatewayProxyRequestEvent?, APIGatewayProxyResponse metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")) metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")) } - LoggingUtils.appendKey("test", "willBeLogged") + MDC.put("test", "willBeLogged") val response = APIGatewayProxyResponseEvent().withHeaders(headers) return try { val pageContents = getPageContents("https://checkip.amazonaws.com") - log.info(pageContents) + log.info("", entry("ip", pageContents)); TracingUtils.putAnnotation("Test", "New") val output = """ { 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 index 36ef72ae7..b1a701b8f 100644 --- 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 @@ -14,6 +14,7 @@ package helloworld; +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; 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; @@ -31,10 +32,10 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; @@ -63,13 +64,13 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); }); - 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); diff --git a/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java index c89545fc9..e0b1a2979 100644 --- a/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java @@ -14,6 +14,7 @@ package helloworld; +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; 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; @@ -31,10 +32,10 @@ import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.slf4j.MDC; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; @@ -44,8 +45,11 @@ * 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); + + @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); @@ -60,13 +64,13 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); }); - 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); 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 index dacd7f1d4..e0b1a2979 100644 --- 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 @@ -14,6 +14,7 @@ package helloworld; +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; 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; @@ -31,10 +32,10 @@ import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.slf4j.MDC; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; @@ -44,14 +45,11 @@ * 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); - // This is controlled by POWERTOOLS_LOGGER_SAMPLE_RATE environment variable - // @Logging(logEvent = true, samplingRate = 0.7) - // This is controlled by POWERTOOLS_METRICS_NAMESPACE environment variable - // @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - // This is controlled by POWERTOOLS_TRACER_CAPTURE_ERROR environment variable + @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); @@ -66,13 +64,13 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); }); - 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); diff --git a/pom.xml b/pom.xml index 7b126a426..b9b8da3bc 100644 --- a/pom.xml +++ b/pom.xml @@ -55,12 +55,12 @@ <module>powertools-large-messages</module> <module>powertools-e2e-tests</module> <module>powertools-batch</module> - <module>examples</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> <properties> diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index c2634533d..58492653a 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -18,15 +18,15 @@ 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; -import software.amazon.lambda.powertools.logging.LoggingUtils; public class Function implements RequestHandler<Input, String> { 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()); return "OK"; diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 41d7e2957..f770679ee 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -91,7 +91,7 @@ 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.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(dr.getPayloadHash()).isEqualTo(""); assertThat(dr.getInProgressExpiryTimestamp()).isEmpty(); assertThat(status).isEqualTo(1); @@ -109,7 +109,7 @@ 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.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(dr.getPayloadHash()).isEqualTo(""); assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo( now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); @@ -228,7 +228,7 @@ 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.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(dr.getPayloadHash()).isEqualTo(""); assertThat(status).isEqualTo(2); assertThat(cache).isEmpty(); @@ -247,11 +247,11 @@ public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessi assertThat(status).isEqualTo(2); assertThat(cache).hasSize(1); - DataRecord record = cache.get("testFunction#7b40f56c086de5aa91dc467456329ed2"); + DataRecord record = cache.get("testFunction#8d6a8f173b46479eff55e0997864a514"); 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.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(record.getPayloadHash()).isEqualTo(""); } @@ -270,7 +270,7 @@ public void getRecord_shouldReturnRecordFromPersistence() Instant now = Instant.now(); DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(record.getResponseData()).isEqualTo("Response"); assertThat(status).isEqualTo(0); @@ -286,15 +286,15 @@ public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() Instant now = Instant.now(); DataRecord dr = new DataRecord( - "testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", + "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", dr); DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); assertThat(record.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); assertThat(record.getResponseData()).isEqualTo("result of the function"); assertThat(status).isEqualTo(-1); // getRecord must not be called (retrieve from cache) @@ -310,15 +310,15 @@ public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() Instant now = Instant.now(); DataRecord dr = new DataRecord( - "testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2", + "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", dr); DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#7b40f56c086de5aa91dc467456329ed2"); + assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); assertThat(record.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(record.getResponseData()).isEqualTo("Response"); assertThat(status).isEqualTo(0); @@ -363,8 +363,8 @@ public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { 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); diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java index 1296a75c7..227eea39e 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java @@ -18,26 +18,15 @@ 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.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import java.util.HashMap; +import java.util.Map; 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.utilities.JsonConfig; - -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; public class IdempotencyFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); - public boolean handlerExecuted = false; public IdempotencyFunction(DynamoDbClient client) { @@ -66,28 +55,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); - } - } + .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-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 index 95086a085..c98da7833 100644 --- 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 @@ -14,8 +14,7 @@ package org.apache.logging.log4j.layout.template.json.resolver; -import static java.lang.Boolean.TRUE; -import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; +import static java.util.Arrays.stream; 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; @@ -26,18 +25,18 @@ import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SAMPLING_RATE; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SERVICE; -import com.fasterxml.jackson.core.JacksonException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; +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.message.Message; 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; /** @@ -170,76 +169,42 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { } }; - /** - * Use a custom message resolver to permit to log json string in json format without escaped quotes. - */ - private static final class MessageResolver implements EventResolver { - private final ObjectMapper mapper = new ObjectMapper() - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); - private final boolean logMessagesAsJsonGlobal; - - public MessageResolver(boolean logMessagesAsJson) { - this.logMessagesAsJsonGlobal = logMessagesAsJson; - } - - public boolean isValidJson(String json) { - if (!(json.startsWith("{") || json.startsWith("["))) { - return false; - } - try { - mapper.readTree(json); - } catch (JacksonException e) { - return false; - } - return true; - } - - @Override - public boolean isResolvable(LogEvent logEvent) { - final Message msg = logEvent.getMessage(); - return null != msg && null != msg.getFormattedMessage(); - } - - @Override - public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { - String message = logEvent.getMessage().getFormattedMessage(); - - String logMessagesAsJsonLocal = logEvent.getContextData().getValue(LOG_MESSAGES_AS_JSON); - Boolean logMessagesAsJson = null; - if (logMessagesAsJsonLocal != null) { - logMessagesAsJson = Boolean.parseBoolean(logMessagesAsJsonLocal); - } - - if (((logMessagesAsJsonGlobal && logMessagesAsJson == null) || TRUE.equals(logMessagesAsJson)) - && isValidJson(message)) { - jsonWriter.writeRawString(message); - } else { - jsonWriter.writeString(message); - } - } - } - + @SuppressWarnings("java:S106") private static final EventResolver NON_POWERTOOLS_FIELD_RESOLVER = (LogEvent logEvent, JsonWriter jsonWriter) -> { StringBuilder stringBuilder = jsonWriter.getStringBuilder(); - // remove dummy field to kick in powertools resolver - stringBuilder.setLength(stringBuilder.length() - 4); - - // Inject all the context information. - ReadOnlyStringMap contextData = logEvent.getContextData(); - contextData.forEach((key, value) -> { - if (!PowertoolsLoggedFields.stringValues().contains(key) && !LOG_MESSAGES_AS_JSON.equals(key)) { - jsonWriter.writeSeparator(); - jsonWriter.writeString(key); - stringBuilder.append(':'); - jsonWriter.writeValue(value); + 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 -> { + serializer.writeRaw(','); + try { + ((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 = Stream.of(new Object[][] { + 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 }, @@ -251,7 +216,7 @@ && isValidJson(message)) { { 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])); + }).collect(Collectors.toMap(data -> (String) data[0], data -> (EventResolver) data[1]))); PowertoolsResolver(final TemplateResolverConfig config) { @@ -259,17 +224,9 @@ && isValidJson(message)) { if (fieldName == null) { internalResolver = NON_POWERTOOLS_FIELD_RESOLVER; } else { - boolean logMessagesAsJson = false; - if (config.exists("asJson")) { - logMessagesAsJson = config.getBoolean("asJson"); - } - if ("message".equals(fieldName)) { - internalResolver = new MessageResolver(logMessagesAsJson); - } else { - internalResolver = eventResolverMap.get(fieldName); - } + internalResolver = eventResolverMap.get(fieldName); if (internalResolver == null) { - throw new IllegalArgumentException("unknown field: " + fieldName); + throw new IllegalArgumentException("Unknown field: " + fieldName); } } } diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json index d8d8810f6..8b811ee5f 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -4,8 +4,7 @@ "field": "name" }, "message": { - "$resolver": "powertools", - "field": "message" + "$resolver": "message" }, "error": { "message": { diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java similarity index 70% rename from powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.java rename to powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java index a00b78906..24014a759 100644 --- a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsMessageResolverTest.java +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java @@ -35,12 +35,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.slf4j.MDC; -import software.amazon.lambda.powertools.logging.LoggingUtils; -import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsJsonMessage; -import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled; +import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments; @Order(2) -class PowertoolsMessageResolverTest { +class PowertoolsResolverArgumentsTest { @Mock private Context context; @@ -67,9 +65,9 @@ void cleanUp() throws IOException { } @Test - void shouldLogJsonMessageWithoutEscapedStringsWhenSettingLogAsJson() { + void shouldLogArgumentsAsJsonWhenUsingRawJson() { // GIVEN - PowertoolsJsonMessage requestHandler = new PowertoolsJsonMessage(); + PowertoolsArguments requestHandler = new PowertoolsArguments(PowertoolsArguments.ArgumentFormat.JSON); SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); msg.setMessageId("1212abcd"); msg.setBody("plop"); @@ -85,24 +83,33 @@ void shouldLogJsonMessageWithoutEscapedStringsWhenSettingLogAsJson() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"message\":{\"messageId\":\"1212abcd\",\"receiptHandle\":null,\"body\":\"plop\",\"md5OfBody\":null,\"md5OfMessageAttributes\":null,\"eventSourceArn\":null,\"eventSource\":\"eb\",\"awsRegion\":\"eu-west-1\",\"attributes\":null,\"messageAttributes\":{\"keyAttribute\":{\"stringValue\":null,\"binaryValue\":null,\"stringListValues\":[\"val1\",\"val2\",\"val3\"],\"binaryListValues\":null,\"dataType\":null}}}") + .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"{\\\"key\\\":\\\"value\\\"}\"") - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") - .doesNotContain(LoggingUtils.LOG_MESSAGES_AS_JSON); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); } @Test - void shouldLogStringMessageWhenNotJson() { + void shouldLogArgumentsAsJsonWhenUsingKeyValue() { // GIVEN - PowertoolsLogEnabled requestHandler = new PowertoolsLogEnabled(); + 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-west-1"); + SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); + attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); + msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); // WHEN - requestHandler.handleRequest(null, context); + requestHandler.handleRequest(msg, context); // THEN File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains("\"message\":\"Test debug event\""); + assertThat(contentOf(logFile)) + .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains("\"message\":\"1212abcd\"") + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); } private void setupContext() { 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 index 1aa98fdef..073cd7026 100644 --- 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 @@ -15,6 +15,7 @@ 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 org.mockito.Mockito.mockStatic; import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; @@ -70,6 +71,13 @@ void shouldResolveAccountId() { 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 void shouldResolveRegion() { try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java similarity index 62% rename from powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java rename to powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java index 3d196e5fb..387074590 100644 --- a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java @@ -14,29 +14,38 @@ package software.amazon.lambda.powertools.logging.internal.handler; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; + 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 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.logging.argument.StructuredArguments; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class PowertoolsJsonMessage implements RequestHandler<SQSEvent.SQSMessage, String> { - private final Logger LOG = LoggerFactory.getLogger(PowertoolsJsonMessage.class); +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 { - LoggingUtils.logMessagesAsJson(true); - LOG.debug(JsonConfig.get().getObjectMapper().writeValueAsString(input)); - - LoggingUtils.logMessagesAsJson(false); - LOG.debug("{\"key\":\"value\"}"); - + MDC.put(CORRELATION_ID.getName(), input.getMessageId()); + if (argumentFormat == ArgumentFormat.JSON) { + LOG.debug("SQS Event", StructuredArguments.json("input", + JsonConfig.get().getObjectMapper().writeValueAsString(input))); + } else { + LOG.debug("SQS Event", StructuredArguments.entry("input", input)); + } LOG.debug("{}", input.getMessageId()); LOG.warn("Message body = {} and id = \"{}\"", input.getBody(), input.getMessageId()); } catch (JsonProcessingException e) { @@ -44,4 +53,8 @@ public String handleRequest(SQSEvent.SQSMessage input, Context context) { } 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 index faa722756..e8c0c5851 100644 --- 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 @@ -18,8 +18,8 @@ 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; -import software.amazon.lambda.powertools.logging.LoggingUtils; public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); @@ -27,8 +27,8 @@ public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { @Override @Logging(clearState = true) public Object handleRequest(Object input, Context context) { - LoggingUtils.appendKey("myKey", "myValue"); + MDC.put("myKey", "myValue"); LOG.debug("Test debug event"); - return null; + return "Bonjour le monde"; } } 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..b98a8eada --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java @@ -0,0 +1,139 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 index 1fc98ec67..a1a7daff1 100644 --- 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 @@ -22,6 +22,9 @@ 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; @@ -29,9 +32,11 @@ 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.logback.internal.LambdaEcsSerializer; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; /** @@ -45,6 +50,29 @@ */ 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 ECS_VERSION = "1.2.0"; protected static final String CLOUD_PROVIDER = "aws"; protected static final String CLOUD_SERVICE = "lambda"; @@ -56,7 +84,7 @@ public class LambdaEcsEncoder extends EncoderBase<ILoggingEvent> { @Override public byte[] headerBytes() { - return null; + return new byte[0]; } /** @@ -65,62 +93,108 @@ public byte[] headerBytes() { * @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(256); - LambdaEcsSerializer.serializeObjectStart(builder); - LambdaEcsSerializer.serializeTimestamp(builder, event.getTimeStamp(), "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "UTC"); - LambdaEcsSerializer.serializeEcsVersion(builder, ECS_VERSION); - LambdaEcsSerializer.serializeLogLevel(builder, event.getLevel()); - LambdaEcsSerializer.serializeFormattedMessage(builder, event.getFormattedMessage()); - IThrowableProxy throwableProxy = event.getThrowableProxy(); - if (throwableProxy != null) { - if (throwableConverter != null) { - LambdaEcsSerializer.serializeException(builder, throwableProxy.getClassName(), - throwableProxy.getMessage(), throwableConverter.convert(event)); - } else if (throwableProxy instanceof ThrowableProxy) { - LambdaEcsSerializer.serializeException(builder, ((ThrowableProxy) throwableProxy).getThrowable()); - } else { - LambdaEcsSerializer.serializeException(builder, throwableProxy.getClassName(), - throwableProxy.getMessage(), throwableProxyConverter.convert(event)); - } + 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()); } - LambdaEcsSerializer.serializeServiceName(builder, LambdaHandlerProcessor.serviceName()); - LambdaEcsSerializer.serializeServiceVersion(builder, mdcPropertyMap.get(FUNCTION_VERSION.getName())); - LambdaEcsSerializer.serializeLoggerName(builder, event.getLoggerName()); - LambdaEcsSerializer.serializeThreadName(builder, event.getThreadName()); - String arn = mdcPropertyMap.get(FUNCTION_ARN.getName()); + 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())); + } + } + private void serializeCloudInfo(JsonSerializer serializer, String arn) { if (includeCloudInfo) { - LambdaEcsSerializer.serializeCloudProvider(builder, CLOUD_PROVIDER); - LambdaEcsSerializer.serializeCloudService(builder, CLOUD_SERVICE); + 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(":"); - LambdaEcsSerializer.serializeCloudRegion(builder, arnParts[3]); - LambdaEcsSerializer.serializeCloudAccountId(builder, arnParts[4]); + serializer.writeRaw(','); + serializer.writeStringField(CLOUD_REGION_ATTR_NAME, arnParts[3]); + serializer.writeRaw(','); + serializer.writeStringField(CLOUD_ACCOUNT_ATTR_NAME, arnParts[4]); } } + } - if (includeFaasInfo) { - LambdaEcsSerializer.serializeFunctionId(builder, arn); - LambdaEcsSerializer.serializeFunctionName(builder, mdcPropertyMap.get(FUNCTION_NAME.getName())); - LambdaEcsSerializer.serializeFunctionVersion(builder, mdcPropertyMap.get(FUNCTION_VERSION.getName())); - LambdaEcsSerializer.serializeFunctionMemory(builder, mdcPropertyMap.get(FUNCTION_MEMORY_SIZE.getName())); - LambdaEcsSerializer.serializeFunctionExecutionId(builder, - mdcPropertyMap.get(FUNCTION_REQUEST_ID.getName())); - LambdaEcsSerializer.serializeColdStart(builder, mdcPropertyMap.get(FUNCTION_COLD_START.getName())); - LambdaEcsSerializer.serializeTraceId(builder, mdcPropertyMap.get(FUNCTION_TRACE_ID.getName())); + 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)); + } } - LambdaEcsSerializer.serializeAdditionalFields(builder, event.getMDCPropertyMap()); - LambdaEcsSerializer.serializeObjectEnd(builder); - return builder.toString().getBytes(UTF_8); } @Override public byte[] footerBytes() { - return null; + return new byte[0]; } /** @@ -196,4 +270,13 @@ public void setIncludeCloudInfo(boolean includeCloudInfo) { 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 index b951e266e..9afaf0ab7 100644 --- 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 @@ -15,7 +15,10 @@ package software.amazon.lambda.powertools.logging.logback; import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; +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; @@ -23,25 +26,40 @@ import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.core.encoder.EncoderBase; -import software.amazon.lambda.powertools.logging.logback.internal.LambdaJsonSerializer; +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. - * It does not use a JSON library but a custom serializer ({@link LambdaJsonSerializer}) */ 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; - private boolean logMessagesAsJsonGlobal; @Override public byte[] headerBytes() { - return null; + return new byte[0]; } @Override @@ -53,43 +71,83 @@ public void start() { } } + @SuppressWarnings("java:S106") @Override public byte[] encode(ILoggingEvent event) { - StringBuilder builder = new StringBuilder(256); - LambdaJsonSerializer.serializeObjectStart(builder); - LambdaJsonSerializer.serializeLogLevel(builder, event.getLevel()); - LambdaJsonSerializer.serializeFormattedMessage( - builder, - event.getFormattedMessage(), - logMessagesAsJsonGlobal, - event.getMDCPropertyMap().get(LOG_MESSAGES_AS_JSON)); + 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) { - LambdaJsonSerializer.serializeException(builder, throwableProxy.getClassName(), + serializeException(serializer, throwableProxy.getClassName(), throwableProxy.getMessage(), throwableConverter.convert(event)); } else if (throwableProxy instanceof ThrowableProxy) { - LambdaJsonSerializer.serializeException(builder, ((ThrowableProxy) throwableProxy).getThrowable()); + Throwable throwable = ((ThrowableProxy) throwableProxy).getThrowable(); + serializeException(serializer, throwable.getClass().getName(), throwable.getMessage(), + Arrays.toString(throwable.getStackTrace())); } else { - LambdaJsonSerializer.serializeException(builder, throwableProxy.getClassName(), - throwableProxy.getMessage(), throwableProxyConverter.convert(event)); + serializeException(serializer, throwableProxy.getClassName(), throwableProxy.getMessage(), throwableProxyConverter.convert( + event)); } } - LambdaJsonSerializer.serializePowertools(builder, event.getMDCPropertyMap(), includePowertoolsInfo); - if (includeThreadInfo) { - LambdaJsonSerializer.serializeThreadName(builder, event.getThreadName()); - LambdaJsonSerializer.serializeThreadId(builder, String.valueOf(Thread.currentThread().getId())); - LambdaJsonSerializer.serializeThreadPriority(builder, String.valueOf(Thread.currentThread().getPriority())); - } - LambdaJsonSerializer.serializeTimestamp(builder, event.getTimeStamp(), timestampFormat, - timestampFormatTimezoneId); - LambdaJsonSerializer.serializeObjectEnd(builder); - return builder.toString().getBytes(UTF_8); } @Override public byte[] footerBytes() { - return null; + return new byte[0]; } /** @@ -191,18 +249,12 @@ public void setIncludePowertoolsInfo(boolean includePowertoolsInfo) { this.includePowertoolsInfo = includePowertoolsInfo; } - /** - * Specify if messages should be logged as JSON, without escaping string (default is <b>false</b>): - * <br/> - * <pre>{@code - * <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> - * <logMessagesAsJson>true</logMessagesAsJson> - * </encoder> - * }</pre> - * - * @param logMessagesAsJson if messages should be looged as JSON (non escaped quotes) - */ - public void setLogMessagesAsJson(boolean logMessagesAsJson) { - this.logMessagesAsJsonGlobal = logMessagesAsJson; + 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/JsonUtils.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java deleted file mode 100644 index e604d10c7..000000000 --- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/JsonUtils.java +++ /dev/null @@ -1,130 +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.logback.internal; - -/** - * Json tools to serialize attributes manually, to avoid using further dependencies (jackson, gson...) - */ -public class JsonUtils { - - private JsonUtils() { - // static utils - } - - protected static void serializeAttribute(StringBuilder builder, String attr, String value, boolean notBegin) { - if (value != null) { - if (notBegin) { - builder.append(","); - } - builder.append("\"").append(attr).append("\":"); - boolean isString = isString(value); - if (isString) { - builder.append("\""); - } - builder.append(value); - if (isString) { - builder.append("\""); - } - } - } - - protected static void serializeAttribute(StringBuilder builder, String attr, String value) { - serializeAttribute(builder, attr, value, true); - } - - protected static void serializeMessage(StringBuilder builder, String attr, String value, boolean logAsJson) { - builder.append(","); - builder.append("\"").append(attr).append("\":"); - if (logAsJson) { - builder.append(value); // log JSON without quotes - } else { - builder.append("\""); - builder.append(value.replace("\"", "\\\"")); // escape quotes in string - builder.append("\""); - } - } - - - protected static void serializeAttributeAsString(StringBuilder builder, String attr, String value, - boolean notBegin) { - if (value != null) { - if (notBegin) { - builder.append(","); - } - builder.append("\"") - .append(attr) - .append("\":\"") - .append(value) - .append("\""); - } - } - - protected static void serializeAttributeAsString(StringBuilder builder, String attr, String value) { - serializeAttributeAsString(builder, attr, value, true); - } - - /** - * 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/internal/LambdaEcsSerializer.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaEcsSerializer.java deleted file mode 100644 index bab1a32fc..000000000 --- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaEcsSerializer.java +++ /dev/null @@ -1,187 +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.logback.internal; - -import ch.qos.logback.classic.Level; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.Map; -import java.util.TimeZone; -import java.util.TreeMap; -import java.util.regex.Matcher; -import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; - -/** - * This class will serialize the log events in ecs format (ElasticSearch).<br/> - * <p> - * Inspired from the ElasticSearch Serializer <code>co.elastic.logging.EcsJsonSerializer</code> - */ -public class LambdaEcsSerializer { - 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 SERVICE_ENV_ATTR_NAME = "service.environment"; - protected static final String EVENT_DATASET_ATTR_NAME = "event.dataset"; - protected static final String FORMATTED_MESSAGE_ATTR_NAME = "message"; - protected static final String THREAD_ATTR_NAME = "process.thread.name"; - protected static final String THREAD_ID_ATTR_NAME = "process.thread.id"; - 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"; - - private LambdaEcsSerializer() {} - - public static void serializeObjectStart(StringBuilder builder) { - builder.append('{'); - } - - public static void serializeObjectEnd(StringBuilder builder) { - builder.append("}\n"); - } - - public static void serializeTimestamp(StringBuilder builder, long timestamp, String timestampFormat, - String timestampFormatTimezoneId) { - 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); - } - JsonUtils.serializeAttributeAsString(builder, TIMESTAMP_ATTR_NAME, formattedTimestamp, false); - } - - public static void serializeThreadName(StringBuilder builder, String threadName) { - if (threadName != null) { - JsonUtils.serializeAttributeAsString(builder, THREAD_ATTR_NAME, threadName); - } - } - - public static void serializeLogLevel(StringBuilder builder, Level level) { - JsonUtils.serializeAttributeAsString(builder, LEVEL_ATTR_NAME, level.toString()); - } - - public static void serializeFormattedMessage(StringBuilder builder, String formattedMessage) { - JsonUtils.serializeAttributeAsString(builder, FORMATTED_MESSAGE_ATTR_NAME, - formattedMessage.replace("\"", Matcher.quoteReplacement("\\\""))); - } - - public static void serializeException(StringBuilder builder, String className, String message, String stackTrace) { - JsonUtils.serializeAttributeAsString(builder, EXCEPTION_MSG_ATTR_NAME, message); - JsonUtils.serializeAttributeAsString(builder, EXCEPTION_CLASS_ATTR_NAME, className); - JsonUtils.serializeAttributeAsString(builder, EXCEPTION_STACK_ATTR_NAME, stackTrace); - } - - public static void serializeException(StringBuilder builder, Throwable throwable) { - serializeException(builder, throwable.getClass().getName(), throwable.getMessage(), - Arrays.toString(throwable.getStackTrace())); - } - - public static void serializeThreadId(StringBuilder builder, String threadId) { - JsonUtils.serializeAttributeAsString(builder, THREAD_ID_ATTR_NAME, threadId); - } - - public static void serializeAdditionalFields(StringBuilder builder, Map<String, String> mdc) { - TreeMap<String, String> sortedMap = new TreeMap<>(mdc); - - sortedMap.forEach((k, v) -> { - if (!PowertoolsLoggedFields.stringValues().contains(k)) { - JsonUtils.serializeAttributeAsString(builder, k, v); - } - }); - } - - public static void serializeEcsVersion(StringBuilder builder, String ecsVersion) { - JsonUtils.serializeAttributeAsString(builder, ECS_VERSION_ATTR_NAME, ecsVersion); - } - - public static void serializeServiceName(StringBuilder builder, String serviceName) { - JsonUtils.serializeAttributeAsString(builder, SERVICE_NAME_ATTR_NAME, serviceName); - } - - public static void serializeServiceVersion(StringBuilder builder, String serviceVersion) { - JsonUtils.serializeAttributeAsString(builder, SERVICE_VERSION_ATTR_NAME, serviceVersion); - } - - public static void serializeLoggerName(StringBuilder builder, String loggerName) { - JsonUtils.serializeAttributeAsString(builder, LOGGER_ATTR_NAME, loggerName); - } - - public static void serializeCloudProvider(StringBuilder builder, String cloudProvider) { - JsonUtils.serializeAttributeAsString(builder, CLOUD_PROVIDER_ATTR_NAME, cloudProvider); - } - - public static void serializeCloudService(StringBuilder builder, String cloudService) { - JsonUtils.serializeAttributeAsString(builder, CLOUD_SERVICE_ATTR_NAME, cloudService); - } - - public static void serializeCloudRegion(StringBuilder builder, String cloudRegion) { - JsonUtils.serializeAttributeAsString(builder, CLOUD_REGION_ATTR_NAME, cloudRegion); - } - - public static void serializeCloudAccountId(StringBuilder builder, String cloudAccountId) { - JsonUtils.serializeAttributeAsString(builder, CLOUD_ACCOUNT_ATTR_NAME, cloudAccountId); - } - - public static void serializeColdStart(StringBuilder builder, String coldStart) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_COLD_START_ATTR_NAME, coldStart); - } - - public static void serializeFunctionExecutionId(StringBuilder builder, String requestId) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_REQUEST_ID_ATTR_NAME, requestId); - } - - public static void serializeFunctionId(StringBuilder builder, String functionArn) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_ARN_ATTR_NAME, functionArn); - } - - public static void serializeFunctionName(StringBuilder builder, String functionName) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_NAME_ATTR_NAME, functionName); - } - - public static void serializeFunctionVersion(StringBuilder builder, String functionVersion) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_VERSION_ATTR_NAME, functionVersion); - } - - public static void serializeFunctionMemory(StringBuilder builder, String functionMemory) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_MEMORY_ATTR_NAME, functionMemory); - } - - public static void serializeTraceId(StringBuilder builder, String traceId) { - JsonUtils.serializeAttributeAsString(builder, FUNCTION_TRACE_ID_ATTR_NAME, traceId); - } -} diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java deleted file mode 100644 index 7d7b8d0d7..000000000 --- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LambdaJsonSerializer.java +++ /dev/null @@ -1,150 +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.logback.internal; - -import static java.lang.Boolean.TRUE; -import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; - -import ch.qos.logback.classic.Level; -import com.fasterxml.jackson.core.JacksonException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.Map; -import java.util.TimeZone; -import java.util.TreeMap; -import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; - -/** - * This class will serialize the log events in json.<br/> - * <p> - * Inspired from the ElasticSearch Serializer co.elastic.logging.EcsJsonSerializer - */ -public class LambdaJsonSerializer { - 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 LambdaJsonSerializer() {} - - public static void serializeObjectStart(StringBuilder builder) { - builder.append('{'); - } - - public static void serializeObjectEnd(StringBuilder builder) { - builder.append("}\n"); - } - - public static void serializeTimestamp(StringBuilder builder, long timestamp, String timestampFormat, - String timestampFormatTimezoneId) { - 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); - } - JsonUtils.serializeAttribute(builder, TIMESTAMP_ATTR_NAME, formattedTimestamp); - } - - public static void serializeThreadName(StringBuilder builder, String threadName) { - if (threadName != null) { - JsonUtils.serializeAttribute(builder, THREAD_ATTR_NAME, threadName); - } - } - - public static void serializeLogLevel(StringBuilder builder, Level level) { - JsonUtils.serializeAttribute(builder, LEVEL_ATTR_NAME, level.toString(), false); - } - - public static void serializeFormattedMessage(StringBuilder builder, String message, - boolean logMessagesAsJsonGlobal, String logMessagesAsJsonLocal) { - Boolean logMessagesAsJson = null; - if (logMessagesAsJsonLocal != null) { - logMessagesAsJson = Boolean.parseBoolean(logMessagesAsJsonLocal); - } - - boolean logAsJson = ((logMessagesAsJsonGlobal && logMessagesAsJson == null) || TRUE.equals(logMessagesAsJson)) - && isValidJson(message); - JsonUtils.serializeMessage(builder, FORMATTED_MESSAGE_ATTR_NAME, message, logAsJson); - } - - public static void serializeException(StringBuilder builder, String className, String message, String stackTrace) { - builder.append(",\"").append(EXCEPTION_ATTR_NAME).append("\":{"); - JsonUtils.serializeAttribute(builder, EXCEPTION_MSG_ATTR_NAME, message, false); - JsonUtils.serializeAttribute(builder, EXCEPTION_CLASS_ATTR_NAME, className); - JsonUtils.serializeAttribute(builder, EXCEPTION_STACK_ATTR_NAME, stackTrace); - builder.append("}"); - } - - public static void serializeException(StringBuilder builder, Throwable throwable) { - serializeException(builder, throwable.getClass().getName(), throwable.getMessage(), - Arrays.toString(throwable.getStackTrace())); - } - - public static void serializeThreadId(StringBuilder builder, String threadId) { - JsonUtils.serializeAttribute(builder, THREAD_ID_ATTR_NAME, threadId); - } - - public static void serializeThreadPriority(StringBuilder builder, String threadPriority) { - JsonUtils.serializeAttribute(builder, THREAD_PRIORITY_ATTR_NAME, threadPriority); - } - - public static void serializePowertools(StringBuilder builder, Map<String, String> mdc, - boolean includePowertoolsInfo) { - TreeMap<String, String> sortedMap = new TreeMap<>(mdc); - sortedMap.forEach((k, v) -> { - if ((includePowertoolsInfo || !PowertoolsLoggedFields.stringValues().contains(k)) // do not log already logged powertools info - && !(k.equals(PowertoolsLoggedFields.SAMPLING_RATE.getName()) && v.equals("0.0")) // do not log sampling rate when 0 - && !LOG_MESSAGES_AS_JSON.equals(k)) // do not log internal keys - { - JsonUtils.serializeAttribute(builder, k, v); - } - }); - - } - - private static final ObjectMapper mapper = new ObjectMapper() - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); - - private static boolean isValidJson(String str) { - if (!(str.startsWith("{") || str.startsWith("["))) { - return false; - } - try { - mapper.readTree(str); - } catch (JacksonException e) { - return false; - } - return true; - } - -} 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 index dc8ac429b..638857cb3 100644 --- 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 @@ -14,13 +14,13 @@ package software.amazon.lambda.powertools.logging.internal; -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +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 static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.logging.LoggingUtils.LOG_MESSAGES_AS_JSON; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -28,11 +28,14 @@ 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.core.JsonProcessingException; +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; @@ -42,7 +45,7 @@ import java.util.Date; import java.util.TimeZone; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -50,10 +53,16 @@ import org.slf4j.LoggerFactory; import org.slf4j.MDC; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder; -import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsJsonMessage; +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.utilities.JsonConfig; +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 { @@ -62,11 +71,6 @@ class LambdaJsonEncoderTest { @Mock private Context context; - @BeforeAll - private static void init() { - JsonConfig.get().getObjectMapper().setSerializationInclusion(NON_NULL); - } - @BeforeEach void setUp() throws IllegalAccessException, IOException { openMocks(this); @@ -97,13 +101,37 @@ void shouldLogInJsonFormat() { // 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:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"function_version\":1,\"myKey\":\"myValue\",\"service\":\"testLogback\",\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"timestamp\":"); + "{\"level\":\"DEBUG\",\"message\":\"Test debug event\",\"cold_start\":true,\"function_arn\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"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-west-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-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains("\"message\":\"1212abcd\"") + .contains("\"message\":\"Message body = plop and id = \"1212abcd\"\""); } @Test - void shouldLogJsonMessageWithoutEscapedStrings() { + void shouldLogArgumentsAsJsonWhenUsingKeyValue() { // GIVEN - PowertoolsJsonMessage requestHandler = new PowertoolsJsonMessage(); + PowertoolsArguments requestHandler = new PowertoolsArguments(PowertoolsArguments.ArgumentFormat.ENTRY); SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); msg.setMessageId("1212abcd"); msg.setBody("plop"); @@ -119,10 +147,9 @@ void shouldLogJsonMessageWithoutEscapedStrings() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"message\":{\"messageId\":\"1212abcd\",\"body\":\"plop\",\"eventSource\":\"eb\",\"awsRegion\":\"eu-west-1\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}}}") + .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") - .doesNotContain(LOG_MESSAGES_AS_JSON); + .contains("\"message\":\"Message body = plop and id = \"1212abcd\"\""); } private final LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "message", null, null); @@ -159,10 +186,9 @@ void shouldNotLogPowertoolsInfo() { } @Test - void shouldLogMessagesAsJsonWhenEnabledInLogbackConfig() throws JsonProcessingException { + void shouldLogStructuredArgumentsAsNewEntries() { // GIVEN LambdaJsonEncoder encoder = new LambdaJsonEncoder(); - encoder.setLogMessagesAsJson(true); SQSEvent.SQSMessage msg = new SQSEvent.SQSMessage(); msg.setMessageId("1212abcd"); @@ -172,24 +198,144 @@ void shouldLogMessagesAsJsonWhenEnabledInLogbackConfig() throws JsonProcessingEx 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 loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, JsonConfig.get().getObjectMapper().writeValueAsString(msg), null, null); + LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "A message", null, new Object[]{argument}); byte[] encoded = encoder.encode(loggingEvent); String result = new String(encoded, StandardCharsets.UTF_8); // THEN (logged as JSON) assertThat(result) - .contains("\"message\":{\"messageId\":\"1212abcd\",\"body\":\"plop\",\"eventSource\":\"eb\",\"awsRegion\":\"eu-west-1\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}}}"); + .contains("\"message\":\"A message\",\"msg\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}"); + } - // WHEN (disabling logging as json) - encoder.setLogMessagesAsJson(false); - encoded = encoder.encode(loggingEvent); - result = new String(encoded, StandardCharsets.UTF_8); + @Test + void shouldLogEventForHandlerWithLogEventAnnotation() { + // GIVEN + PowertoolsLogEvent requestHandler = new PowertoolsLogEvent(); - // THEN (logged as String) - assertThat(result) - .contains("\"message\":\"{\\\"messageId\\\":\\\"1212abcd\\\",\\\"body\\\":\\\"plop\\\",\\\"eventSource\\\":\\\"eb\\\",\\\"awsRegion\\\":\\\"eu-west-1\\\",\\\"messageAttributes\\\":{\\\"keyAttribute\\\":{\\\"stringListValues\\\":[\\\"val1\\\",\\\"val2\\\",\\\"val3\\\"]}}}\""); + // 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-west-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-west-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\"") + .contains("\"event\":\"{\"key\":\"value\"}\""); // logged as String for StreamHandler + } + + @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 @@ -238,7 +384,10 @@ void shouldLogException() { String result = new String(encoded, StandardCharsets.UTF_8); // THEN - assertThat(result).contains("\"message\":\"Error\",\"error\":{\"message\":\"Unexpected value\",\"name\":\"java.lang.IllegalStateException\",\"stack\":\"[software.amazon.lambda.powertools.logging.internal.LambdaJsonEncoderTest.shouldLogException"); + 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(); @@ -249,7 +398,9 @@ void shouldLogException() { result = new String(encoded, StandardCharsets.UTF_8); // THEN (stack is logged with root cause first) - assertThat(result).contains("\"message\":\"Error\",\"error\":{\"message\":\"Unexpected value\",\"name\":\"java.lang.IllegalStateException\",\"stack\":\"java.lang.IllegalStateException: Unexpected value\n"); + assertThat(result).contains("\"message\":\"Unexpected value\"") + .contains("\"name\":\"java.lang.IllegalStateException\"") + .contains("\"stack\":\"java.lang.IllegalStateException: Unexpected value\n"); } private void setupContext() { diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java similarity index 62% rename from powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java rename to powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java index fdc279319..387074590 100644 --- a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsJsonMessage.java +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsArguments.java @@ -14,26 +14,38 @@ package software.amazon.lambda.powertools.logging.internal.handler; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; + 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 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.logging.argument.StructuredArguments; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class PowertoolsJsonMessage implements RequestHandler<SQSEvent.SQSMessage, String> { - private final Logger LOG = LoggerFactory.getLogger(PowertoolsJsonMessage.class); +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 { - LoggingUtils.logMessagesAsJson(true); - LoggingUtils.setCorrelationId(input.getMessageId()); - LOG.debug(JsonConfig.get().getObjectMapper().writeValueAsString(input)); + MDC.put(CORRELATION_ID.getName(), input.getMessageId()); + if (argumentFormat == ArgumentFormat.JSON) { + LOG.debug("SQS Event", StructuredArguments.json("input", + JsonConfig.get().getObjectMapper().writeValueAsString(input))); + } else { + LOG.debug("SQS Event", StructuredArguments.entry("input", input)); + } LOG.debug("{}", input.getMessageId()); LOG.warn("Message body = {} and id = \"{}\"", input.getBody(), input.getMessageId()); } catch (JsonProcessingException e) { @@ -41,4 +53,8 @@ public String handleRequest(SQSEvent.SQSMessage input, Context context) { } 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 index faa722756..e8c0c5851 100644 --- 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 @@ -18,8 +18,8 @@ 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; -import software.amazon.lambda.powertools.logging.LoggingUtils; public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); @@ -27,8 +27,8 @@ public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { @Override @Logging(clearState = true) public Object handleRequest(Object input, Context context) { - LoggingUtils.appendKey("myKey", "myValue"); + MDC.put("myKey", "myValue"); LOG.debug("Test debug event"); - return null; + return "Bonjour le monde"; } } diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.java new file mode 100644 index 000000000..14a7874cb --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEvent.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 PowertoolsLogEvent implements RequestHandler<Object, Object> { + + @Override + @Logging(logEvent = true) + public Object handleRequest(Object input, Context context) { + return null; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventDisabled.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventDisabled.java similarity index 90% rename from powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventDisabled.java rename to powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventDisabled.java index fc1feb52d..8171bee3e 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventDisabled.java +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventDisabled.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.RequestHandler; @@ -20,8 +20,8 @@ public class PowertoolsLogEventDisabled implements RequestHandler<Object, Object> { - @Logging @Override + @Logging(logEvent = false) public Object handleRequest(Object input, Context context) { return null; } diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.java new file mode 100644 index 000000000..443051204 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/handler/PowertoolsLogEventForStream.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.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowertoolsLogEventForStream implements RequestStreamHandler { + + @Override + @Logging(logEvent = true) + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + 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/src/main/java/software/amazon/lambda/powertools/logging/Logging.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java index 05a9cfe31..9e5e735d1 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 @@ -57,7 +57,8 @@ * <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>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) 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 d7ceb8ccd..000000000 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.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.logging; - -import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Arrays; -import java.util.Map; -import org.slf4j.MDC; -import software.amazon.lambda.powertools.utilities.JsonConfig; - -/** - * A class of helper functions to add functionality to Logging. - * Adding/removing keys is based on <a href="https://www.slf4j.org/manual.html#mdc">MDC</a>, which is ThreadSafe. - */ -public final class LoggingUtils { - - public static final String LOG_MESSAGES_AS_JSON = "PowertoolsLogMessagesAsJson"; - - 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) { - MDC.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) { - customKeys.forEach(MDC::put); - } - - /** - * Remove an additional key from log entry. - * - * @param customKey The name of the key to be logged - */ - public static void removeKey(String customKey) { - MDC.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) { - Arrays.stream(keys).forEach(MDC::remove); - } - - /** - * Sets correlation id attribute on the logs. - * - * @param value The value of the correlation id - */ - public static void setCorrelationId(String value) { - MDC.put(CORRELATION_ID.getName(), value); - } - - /** - * Get correlation id attribute. Maybe null. - * @return correlation id set `@Logging(correlationIdPath="JMESPATH Expression")` or `LoggingUtils.setCorrelationId("value")` - */ - public static String getCorrelationId() { - return MDC.get(CORRELATION_ID.getName()); - } - - /** - * When set to true, will log messages as JSON (without escaping string). - * Useful to log events or big JSON objects. - * @param value boolean to specify if yes or no messages should be logged as JSON (default is false) - */ - public static void logMessagesAsJson(boolean value) { - MDC.put(LOG_MESSAGES_AS_JSON, String.valueOf(value)); - } - - /** - * Sets the instance of ObjectMapper object which is used for serialising event when - * {@code @Logging(logEvent = true, logResponse = true)}. - * - * Not Thread Safe, the object mapper is static, changing it in different threads can lead to unexpected behaviour - * - * @param objectMapper Custom implementation of object mapper to be used for logging serialised event - */ - public static void setObjectMapper(ObjectMapper objectMapper) { - LoggingUtils.objectMapper = objectMapper; - } - - public static ObjectMapper getObjectMapper() { - if (LoggingUtils.objectMapper == null) { - LoggingUtils.objectMapper = JsonConfig.get().getObjectMapper(); - } - return LoggingUtils.objectMapper; - } -} 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..28b29146e --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/ArrayArgument.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.argument; + +import java.util.Objects; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#array(String, Object...)} + */ +public 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..e14f23788 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/JsonArgument.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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)} + */ +public 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..569667419 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/KeyValueArgument.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.util.Objects; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#entry(String, Object)} + */ +public 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..9a06ea095 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/MapArgument.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.argument; + +import java.util.HashMap; +import java.util.Map; +import software.amazon.lambda.powertools.logging.internal.JsonSerializer; + +/** + * See {@link StructuredArguments#entries(Map)} + */ +public 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 (Map.Entry<?, ?> entry : map.entrySet()) { + serializer.writeObjectField(String.valueOf(entry.getKey()), entry.getValue()); + } + } + } + + @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..21fea068d --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArgument.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.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..8a75b3118 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/argument/StructuredArguments.java @@ -0,0 +1,129 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.Map; + +/** + * Factory for creating {@link StructuredArgument}s. + * Inspired from the StructuredArgument of logstash-logback-encoder. + */ +public class StructuredArguments { + + private StructuredArguments() { + // nothing to do, use static methods only + } + + /** + * Adds "key": "value" to the JSON structure and "key=value" to the formatted message. + * + * @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 + */ + public static StructuredArgument entry(String key, Object value) { + 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. + * + * @param map {@link Map} holding the key/value pairs + * @return a {@link MapArgument} populated with the data + */ + public static StructuredArgument entries(Map<?, ?> map) { + return new MapArgument(map); + } + + /** + * 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]} + * + * @param key the field name + * @param values elements of the array associated with the key + * @return an {@link ArrayArgument} populated with the data + */ + public static StructuredArgument array(String key, Object... values) { + return new ArrayArgument(key, values); + } + + /** + * Adds the {@code rawJson} to the JSON structure and + * the {@code rawJson} to the formatted message. + * + * @param key the field name + * @param rawJson the raw JSON String + * @return a {@link JsonArgument} populated with the data + */ + public static StructuredArgument json(String key, String rawJson) { + 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/JsonSerializer.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JsonSerializer.java new file mode 100644 index 000000000..0b4359825 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JsonSerializer.java @@ -0,0 +1,599 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { + builder.append("\"").append(text).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/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java index 48f67700d..eccfdae4f 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,8 +15,6 @@ 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.common.internal.LambdaHandlerProcessor.coldStartDone; import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.extractContext; import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.getXrayTraceId; @@ -25,20 +23,19 @@ 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.common.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.argument.StructuredArguments.entry; import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_LEVEL; 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_LEVEL; import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_RESPONSE; import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_SAMPLING_RATE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; 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.SERVICE; import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import io.burt.jmespath.Expression; import java.io.ByteArrayInputStream; @@ -54,7 +51,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.Random; import java.util.ServiceLoader; import org.aspectj.lang.ProceedingJoinPoint; @@ -68,7 +64,6 @@ import org.slf4j.MarkerFactory; import org.slf4j.event.Level; import software.amazon.lambda.powertools.logging.Logging; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.utilities.JsonConfig; @@ -188,8 +183,9 @@ public Object around(ProceedingJoinPoint pjp, addLambdaContextToLoggingContext(pjp); - getXrayTraceId().ifPresent(xRayTraceId -> appendKey(FUNCTION_TRACE_ID.getName(), xRayTraceId)); + getXrayTraceId().ifPresent(xRayTraceId -> MDC.put(FUNCTION_TRACE_ID.getName(), xRayTraceId)); + // Log Event Object[] proceedArgs = logEvent(pjp, logging, isOnRequestHandler, isOnRequestStreamHandler); if (!logging.correlationIdPath().isEmpty()) { @@ -210,6 +206,7 @@ public Object around(ProceedingJoinPoint pjp, Object lambdaFunctionResponse; try { + // Call Function Handler lambdaFunctionResponse = pjp.proceed(proceedArgs); } catch (Throwable t) { if (logging.logError() || POWERTOOLS_LOG_ERROR) { @@ -224,6 +221,7 @@ public Object around(ProceedingJoinPoint pjp, coldStartDone(); } + // Log Response if ((logging.logResponse() || POWERTOOLS_LOG_RESPONSE)) { if (isOnRequestHandler) { logRequestHandlerResponse(pjp, lambdaFunctionResponse); @@ -255,9 +253,9 @@ private void addLambdaContextToLoggingContext(ProceedingJoinPoint pjp) { Context extractedContext = extractContext(pjp); if (extractedContext != null) { - appendKeys(PowertoolsLoggedFields.setValuesFromLambdaContext(extractedContext)); - appendKey(FUNCTION_COLD_START.getName(), isColdStart() ? "true" : "false"); - appendKey(SERVICE.getName(), serviceName()); + PowertoolsLoggedFields.setValuesFromLambdaContext(extractedContext).forEach(MDC::put); + MDC.put(FUNCTION_COLD_START.getName(), isColdStart() ? "true" : "false"); + MDC.put(SERVICE.getName(), serviceName()); } } @@ -273,7 +271,7 @@ private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, return; } - appendKey(PowertoolsLoggedFields.SAMPLING_RATE.getName(), String.valueOf(samplingRate)); + MDC.put(PowertoolsLoggedFields.SAMPLING_RATE.getName(), String.valueOf(samplingRate)); if (samplingRate == 0) { return; @@ -305,49 +303,45 @@ private double samplingRate(final Logging logging) { return logging.samplingRate(); } + @SuppressWarnings("java:S3457") private void logRequestHandlerEvent(final ProceedingJoinPoint pjp, final Object event) { Logger log = logger(pjp); if (log.isInfoEnabled()) { - LoggingUtils.logMessagesAsJson(true); - asJson(event).ifPresent(log::info); - LoggingUtils.logMessagesAsJson(false); + log.info("Handler Event", entry("event", event)); } } + @SuppressWarnings("java:S3457") private Object[] logRequestStreamHandlerEvent(final ProceedingJoinPoint pjp) { Object[] args = pjp.getArgs(); Logger log = logger(pjp); if (log.isInfoEnabled()) { - LoggingUtils.logMessagesAsJson(true); try { byte[] bytes = bytesFromInputStreamSafely((InputStream) pjp.getArgs()[0]); args[0] = new ByteArrayInputStream(bytes); // do not log asJson as it can be something else (String, XML...) - log.info("{}", new String(bytes, UTF_8)); + 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); } - LoggingUtils.logMessagesAsJson(false); } return args; } + @SuppressWarnings("java:S3457") private void logRequestHandlerResponse(final ProceedingJoinPoint pjp, final Object response) { Logger log = logger(pjp); if (log.isInfoEnabled()) { - LoggingUtils.logMessagesAsJson(true); - asJson(response).ifPresent(log::info); - LoggingUtils.logMessagesAsJson(false); + log.info("Handler Response", entry("response", response)); } } + @SuppressWarnings("java:S3457") private void logRequestStreamHandlerResponse(final ProceedingJoinPoint pjp, final byte[] bytes) { Logger log = logger(pjp); if (log.isInfoEnabled()) { - LoggingUtils.logMessagesAsJson(true); // we do not log with asJson as it can be something else (String, XML, ...) - log.info("{}", new String(bytes, UTF_8)); - LoggingUtils.logMessagesAsJson(false); + log.info("Handler Response", entry("response", new String(bytes, UTF_8))); } } @@ -356,12 +350,12 @@ private void captureCorrelationId(final String correlationIdPath, final boolean isOnRequestHandler, final boolean isOnRequestStreamHandler) { if (isOnRequestHandler) { - JsonNode jsonNode = LoggingUtils.getObjectMapper().valueToTree(proceedArgs[0]); + JsonNode jsonNode = JsonConfig.get().getObjectMapper().valueToTree(proceedArgs[0]); setCorrelationIdFromNode(correlationIdPath, jsonNode); } else if (isOnRequestStreamHandler) { try { byte[] bytes = bytesFromInputStreamSafely((InputStream) proceedArgs[0]); - JsonNode jsonNode = LoggingUtils.getObjectMapper().readTree(bytes); + JsonNode jsonNode = JsonConfig.get().getObjectMapper().readTree(bytes); proceedArgs[0] = new ByteArrayInputStream(bytes); setCorrelationIdFromNode(correlationIdPath, jsonNode); @@ -377,7 +371,7 @@ private void setCorrelationIdFromNode(String correlationIdPath, JsonNode jsonNod String asText = node.asText(); if (null != asText && !asText.isEmpty()) { - LoggingUtils.setCorrelationId(asText); + MDC.put(CORRELATION_ID.getName(), asText); } else { LOG.warn("Unable to extract any correlation id. Is your function expecting supported event type?"); } @@ -398,15 +392,6 @@ private byte[] bytesFromInputStreamSafely(final InputStream inputStream) throws } } - private Optional<String> asJson(final Object target) { - try { - return ofNullable(LoggingUtils.getObjectMapper().writeValueAsString(target)); - } catch (JsonProcessingException e) { - LOG.error("Failed logging object of type {}", target.getClass(), e); - return empty(); - } - } - private Logger logger(final ProceedingJoinPoint pjp) { return LoggerFactory.getLogger(pjp.getSignature().getDeclaringType()); } diff --git a/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java b/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java index 2a9322592..acc635e75 100644 --- a/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java +++ b/powertools-logging/src/test/java/org/slf4j/test/TestLogger.java @@ -185,6 +185,9 @@ public class TestLogger extends LegacyAbstractLogger { /** 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. @@ -368,6 +371,8 @@ protected void handleNormalizedLoggingCall(Level level, Marker marker, String me 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 @@ -449,4 +454,11 @@ protected String getFullyQualifiedCallerName() { return null; } + public Object[] getArguments() { + return arguments; + } + + public void clearArguments() { + arguments = null; + } } 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 04e977c58..000000000 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java +++ /dev/null @@ -1,114 +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 com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.MDC; -import software.amazon.lambda.powertools.utilities.JsonConfig; - - -class LoggingUtilsTest { - - @BeforeEach - void setUp() { - MDC.clear(); - } - - @Test - void shouldSetCustomKeyInLoggingContext() { - LoggingUtils.appendKey("org/slf4j/test", "value"); - - assertThat(MDC.getCopyOfContextMap()) - .hasSize(1) - .containsEntry("org/slf4j/test", "value"); - } - - @Test - void shouldSetCustomKeyAsMapInLoggingContext() { - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("org/slf4j/test", "value"); - customKeys.put("test1", "value1"); - - LoggingUtils.appendKeys(customKeys); - - assertThat(MDC.getCopyOfContextMap()) - .hasSize(2) - .containsEntry("org/slf4j/test", "value") - .containsEntry("test1", "value1"); - } - - @Test - void shouldRemoveCustomKeyInLoggingContext() { - LoggingUtils.appendKey("org/slf4j/test", "value"); - - assertThat(MDC.getCopyOfContextMap()) - .hasSize(1) - .containsEntry("org/slf4j/test", "value"); - - LoggingUtils.removeKey("org/slf4j/test"); - - assertThat(MDC.getCopyOfContextMap()) - .isEmpty(); - } - - @Test - void shouldRemoveCustomKeysInLoggingContext() { - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("org/slf4j/test", "value"); - customKeys.put("test1", "value1"); - - LoggingUtils.appendKeys(customKeys); - - assertThat(MDC.getCopyOfContextMap()) - .hasSize(2) - .containsEntry("org/slf4j/test", "value") - .containsEntry("test1", "value1"); - - LoggingUtils.removeKeys("org/slf4j/test", "test1"); - - assertThat(MDC.getCopyOfContextMap()) - .isEmpty(); - } - - @Test - void shouldAddCorrelationIdToLoggingContext() { - String id = "correlationID_12345"; - LoggingUtils.setCorrelationId(id); - - assertThat(MDC.getCopyOfContextMap()) - .hasSize(1) - .containsEntry("correlation_id", id); - - assertThat(LoggingUtils.getCorrelationId()).isEqualTo(id); - } - - @Test - void shouldGetObjectMapper() { - assertThat(LoggingUtils.getObjectMapper()).isNotNull(); - assertThat(LoggingUtils.getObjectMapper()).isEqualTo(JsonConfig.get().getObjectMapper()); - - ObjectMapper mapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - LoggingUtils.setObjectMapper(mapper); - assertThat(LoggingUtils.getObjectMapper()).isEqualTo(mapper); - - } -} \ No newline at end of file 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..64200e640 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/argument/StructuredArgumentsTest.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.logging.argument; + +import static org.assertj.core.api.Assertions.assertThat; + +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 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}"); + } + +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java index e1829a777..cb7fbb408 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogClearState.java @@ -19,8 +19,8 @@ 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 PowertoolsLogClearState implements RequestHandler<Map<String, String>, Object> { private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogClearState.class); @@ -28,7 +28,7 @@ public class PowertoolsLogClearState implements RequestHandler<Map<String, Strin @Override @Logging(clearState = true) public Object handleRequest(Map<String, String> input, Context context) { - LoggingUtils.appendKey("mySuperSecret", input.get("mySuperSecret")); + MDC.put("mySuperSecret", input.get("mySuperSecret")); LOG.info("Test event"); return null; } 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 index d6c79a445..aa0a5942c 100644 --- 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 @@ -21,7 +21,7 @@ import software.amazon.lambda.powertools.logging.Logging; public class PowertoolsLogEnabled implements RequestHandler<Object, Object> { - private final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); private final boolean throwError; public PowertoolsLogEnabled(boolean throwError) { @@ -49,4 +49,8 @@ public Object handleRequest(Object input, Context context) { 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/PowertoolsLogEvent.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEvent.java index 87677d601..c83692e95 100644 --- 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 @@ -16,13 +16,21 @@ 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/PowertoolsLogEventEnvVar.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventEnvVar.java new file mode 100644 index 000000000..230394bf7 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventEnvVar.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 PowertoolsLogEventEnvVar implements RequestHandler<Object, Object> { + + private final Logger logger = LoggerFactory.getLogger(PowertoolsLogEventEnvVar.class); + + @Override + @Logging + public Object handleRequest(Object input, Context context) { + return null; + } + + public Logger getLogger() { + return logger; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java index 350b29cde..6e27f47ce 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventForStream.java @@ -21,14 +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; public class PowertoolsLogEventForStream implements RequestStreamHandler { + private static final Logger logger = LoggerFactory.getLogger(PowertoolsLogEventForStream.class); + @Override @Logging(logEvent = true) public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { ObjectMapper mapper = new ObjectMapper(); 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 index 001bde3ed..e7607d3c9 100644 --- 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 @@ -16,9 +16,16 @@ 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) 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 index 38be5c025..fe591627b 100644 --- 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 @@ -19,10 +19,14 @@ 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 { @@ -32,4 +36,8 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream, Co outputStream.write(buf, 0, length); } } + + public static Logger getLogger() { + return logger; + } } 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/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index bc5e53675..28e20aadd 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 @@ -15,7 +15,6 @@ 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; @@ -52,15 +51,14 @@ 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.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -70,8 +68,10 @@ import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.slf4j.event.Level; +import org.slf4j.test.TestLogger; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.common.internal.SystemWrapper; +import software.amazon.lambda.powertools.logging.argument.KeyValueArgument; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId; @@ -84,7 +84,7 @@ import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogError; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEvent; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventDisabled; +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; @@ -466,51 +466,61 @@ void shouldLogxRayTraceIdEnvVarSet() { void shouldLogEventForHandlerWithLogEventAnnotation() { // GIVEN requestHandler = new PowertoolsLogEvent(); + List<String> listOfOneElement = singletonList("ListOfOneElement"); // WHEN - requestHandler.handleRequest(singletonList("ListOfOneElement"), context); + requestHandler.handleRequest(listOfOneElement, context); // THEN - File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains("[\"ListOfOneElement\"]"); + TestLogger logger = (TestLogger) PowertoolsLogEvent.getLogger(); + assertThat(logger.getArguments()).hasSize(1); + KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; + assertThat(argument.getKey()).isEqualTo("event"); + assertThat(argument.getValue()).isEqualTo(listOfOneElement); } @Test - void shouldLogEventForHandlerWhenEnvVariableSetToTrue() throws IllegalAccessException { - try { - // GIVEN - LoggingConstants.POWERTOOLS_LOG_EVENT = true; + void shouldLogEventForHandlerWhenEnvVariableSetToTrue() { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_EVENT = true; - requestHandler = new PowertoolsLogEnabled(); + requestHandler = new PowertoolsLogEventEnvVar(); - SQSEvent.SQSMessage message = new SQSEvent.SQSMessage(); - message.setBody("body"); - message.setMessageId("1234abcd"); - message.setAwsRegion("eu-west-1"); + SQSEvent.SQSMessage message = new SQSEvent.SQSMessage(); + message.setBody("body"); + message.setMessageId("1234abcd"); + message.setAwsRegion("eu-west-1"); - // WHEN - requestHandler.handleRequest(message, context); + // WHEN + requestHandler.handleRequest(message, context); - // THEN - File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains("\"body\":\"body\"").contains("\"messageId\":\"1234abcd\"").contains("\"awsRegion\":\"eu-west-1\""); + // THEN + TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar)requestHandler).getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; + assertThat(argument.getKey()).isEqualTo("event"); + assertThat(argument.getValue()).isEqualTo(message); } finally { LoggingConstants.POWERTOOLS_LOG_EVENT = false; + if (logger != null){ + logger.clearArguments(); + } } } @Test - void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() throws IOException { + void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() { // GIVEN LoggingConstants.POWERTOOLS_LOG_EVENT = false; // WHEN - requestHandler = new PowertoolsLogEventDisabled(); + requestHandler = new PowertoolsLogEventEnvVar(); requestHandler.handleRequest(singletonList("ListOfOneElement"), context); // THEN - Assertions.assertEquals(0, - Files.lines(Paths.get("target/logfile.json")).collect(joining()).length()); + TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar)requestHandler).getLogger(); + assertThat(logger.getArguments()).isNull(); } @Test @@ -518,16 +528,24 @@ void shouldLogEventForStreamHandler() throws IOException { // GIVEN requestStreamHandler = new PowertoolsLogEventForStream(); ByteArrayOutputStream output = new ByteArrayOutputStream(); + Map<String, String> map = Collections.singletonMap("key", "value"); // WHEN - requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(Collections.singletonMap("key", "value"))), output, context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(map)), output, context); // THEN assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isNotEmpty(); - File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains("{\"key\":\"value\"}"); + TestLogger logger = (TestLogger) PowertoolsLogEventForStream.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; + assertThat(argument.getKey()).isEqualTo("event"); + assertThat(argument.getValue()).isEqualTo("{\"key\":\"value\"}"); + } finally { + logger.clearArguments(); + } } @Test @@ -539,26 +557,37 @@ void shouldLogResponseForHandlerWithLogResponseAnnotation() { requestHandler.handleRequest("input", context); // THEN - File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains("Hola mundo"); + TestLogger logger = (TestLogger) PowertoolsLogResponse.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; + assertThat(argument.getKey()).isEqualTo("response"); + assertThat(argument.getValue()).isEqualTo("Hola mundo"); + } finally { + logger.clearArguments(); + } } @Test - void shouldLogResponseForHandlerWhenEnvVariableSetToTrue() throws IllegalAccessException { - try { - // GIVEN - LoggingConstants.POWERTOOLS_LOG_RESPONSE = true; + void shouldLogResponseForHandlerWhenEnvVariableSetToTrue() { + // GIVEN + LoggingConstants.POWERTOOLS_LOG_RESPONSE = true; - requestHandler = new PowertoolsLogEnabled(); + requestHandler = new PowertoolsLogEnabled(); - // WHEN - requestHandler.handleRequest("input", context); + // WHEN + requestHandler.handleRequest("input", context); - // THEN - File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains("Bonjour le monde"); + // THEN + TestLogger logger = (TestLogger) PowertoolsLogEnabled.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; + assertThat(argument.getKey()).isEqualTo("response"); + assertThat(argument.getValue()).isEqualTo("Bonjour le monde"); } finally { LoggingConstants.POWERTOOLS_LOG_RESPONSE = false; + logger.clearArguments(); } } @@ -576,8 +605,15 @@ void shouldLogResponseForStreamHandler() throws IOException { assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isEqualTo(input); - File logFile = new File("target/logfile.json"); - assertThat(contentOf(logFile)).contains(input); + TestLogger logger = (TestLogger) PowertoolsLogResponseForStream.getLogger(); + try { + assertThat(logger.getArguments()).hasSize(1); + KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; + assertThat(argument.getKey()).isEqualTo("response"); + assertThat(argument.getValue()).isEqualTo(input); + } finally { + logger.clearArguments(); + } } @Test @@ -598,7 +634,7 @@ void shouldLogErrorForHandlerWithLogErrorAnnotation() { } @Test - void shouldLogErrorForHandlerWhenEnvVariableSetToTrue() throws IllegalAccessException { + void shouldLogErrorForHandlerWhenEnvVariableSetToTrue() { try { // GIVEN LoggingConstants.POWERTOOLS_LOG_ERROR = true; 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-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 e961f21fa..fc0f083e5 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,29 +14,51 @@ package software.amazon.lambda.powertools.utilities; +import com.fasterxml.jackson.annotation.JsonInclude; +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 io.burt.jmespath.JmesPath; import io.burt.jmespath.RuntimeConfiguration; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionRegistry; import io.burt.jmespath.jackson.JacksonRuntime; +import java.util.function.Supplier; import software.amazon.lambda.powertools.utilities.jmespath.Base64Function; 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. + .serializationInclusion(JsonInclude.Include.NON_NULL) + // 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) + .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() ); + private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() .withSilentTypeErrors(true) .withFunctionRegistry(customFunctions) .build(); + private JmesPath<JsonNode> jmesPath = new JacksonRuntime(configuration, getObjectMapper()); private JsonConfig() { 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..2914bd286 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 @@ -39,7 +39,6 @@ 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 software.amazon.lambda.powertools.utilities.model.Order; import software.amazon.lambda.powertools.utilities.model.Product; @@ -93,14 +92,6 @@ public void testDeserializeAPIGWEventBodyAsObject_shouldReturnObject(APIGatewayP 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) { @@ -164,14 +155,6 @@ public void testDeserializeEmptyEventAsList_shouldThrowException() { .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) { diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index ee39b5d0f..a4eb3756f 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -71,6 +71,9 @@ <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--> @@ -185,10 +188,6 @@ <Match> <Bug pattern="EI_EXPOSE_STATIC_REP2"/> <Or> - <And> - <Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/> - <Method name="setObjectMapper"/> - </And> <And> <Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/> <Method name="defaultObjectMapper"/> @@ -206,10 +205,6 @@ <Match> <Bug pattern="MS_EXPOSE_REP"/> <Or> - <And> - <Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/> - <Method name="getObjectMapper"/> - </And> <And> <Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/> <Method name="objectMapper"/> From 41a6eb3b266c8efe447d9669c614ab425b65826a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 12:16:18 +0100 Subject: [PATCH 0579/1008] build(deps): bump commons-io:commons-io from 2.13.0 to 2.15.1 (#1590) Bumps commons-io:commons-io from 2.13.0 to 2.15.1. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 9d84ce9f2..92d8f8291 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -103,7 +103,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.13.0</version> + <version>2.15.1</version> </dependency> <dependency> From ffe2091f5b921a2d1211b817a47a03f85fb057a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 12:17:20 +0100 Subject: [PATCH 0580/1008] build(deps): bump org.apache.maven.plugins:maven-surefire-plugin (#1596) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.2 to 3.2.5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.2.5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- pom.xml | 4 ++-- powertools-logging/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 7fe562072..4784dc5e0 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -124,7 +124,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.2.5</version> <configuration> <environmentVariables> <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index c841c0f38..64637be0e 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -120,7 +120,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.2.5</version> <configuration> <systemPropertyVariables> <sqlite4java.library.path>${project.build.directory}/native-libs</sqlite4java.library.path> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 2aa813ff4..e5147acc4 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -41,7 +41,7 @@ <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> + <version>3.2.5</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/pom.xml b/pom.xml index 2ad359a3a..f9105b9d6 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <maven-compiler-plugin.version>3.11.0</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> + <maven-surefire-plugin.version>3.2.5</maven-surefire-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.6.0</maven-javadoc-plugin.version> @@ -527,7 +527,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.2.5</version> <configuration> <argLine> @{argLine} diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 78c36f41a..656b9ee81 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -140,7 +140,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.2.5</version> <configuration> <environmentVariables> <AWS_LAMBDA_LOG_FORMAT>JSON</AWS_LAMBDA_LOG_FORMAT> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 788ac9438..49f0cd80c 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -141,7 +141,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.2.5</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> From 67b122723553fa2780cf784994527112d14937a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 12:21:02 +0100 Subject: [PATCH 0581/1008] build(deps): bump aws.sdk.version from 2.24.5 to 2.25.1 (#1595) Bumps `aws.sdk.version` from 2.24.5 to 2.25.1. Updates `software.amazon.awssdk:bom` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:http-client-spi` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:url-connection-client` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:dynamodb` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:s3` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:lambda` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:kinesis` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:cloudwatch` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:xray` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:sqs` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:cloudformation` from 2.24.5 to 2.25.1 Updates `software.amazon.awssdk:sts` from 2.24.5 to 2.25.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 795834a8f..0e0b5cbf7 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.24.5</aws.sdk.version> + <aws.sdk.version>2.25.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index b9b8da3bc..4e80d90ed 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.16.0</jackson.version> - <aws.sdk.version>2.24.5</aws.sdk.version> + <aws.sdk.version>2.25.1</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 8f176ccd89aba0772779125d6b7e388018ddc269 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:45:01 +0100 Subject: [PATCH 0582/1008] build(deps): bump aws.sdk.version from 2.25.1 to 2.25.6 (#1600) Bumps `aws.sdk.version` from 2.25.1 to 2.25.6. Updates `software.amazon.awssdk:bom` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:http-client-spi` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:url-connection-client` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:dynamodb` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:s3` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:lambda` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:kinesis` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:cloudwatch` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:xray` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:sqs` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:cloudformation` from 2.25.1 to 2.25.6 Updates `software.amazon.awssdk:sts` from 2.25.1 to 2.25.6 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 0e0b5cbf7..107c8df90 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.25.1</aws.sdk.version> + <aws.sdk.version>2.25.6</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 4e80d90ed..aaf7ae002 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.16.0</jackson.version> - <aws.sdk.version>2.25.1</aws.sdk.version> + <aws.sdk.version>2.25.6</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 8dec2c9b14e41485426ed45c7871701b784d28a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:59:53 +0100 Subject: [PATCH 0583/1008] build(deps): bump aws.sdk.version from 2.24.10 to 2.25.6 (#1603) Bumps `aws.sdk.version` from 2.24.10 to 2.25.6. Updates `software.amazon.awssdk:bom` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:http-client-spi` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:url-connection-client` from 2.21.1 to 2.25.6 Updates `software.amazon.awssdk:sqs` from 2.21.1 to 2.25.6 Updates `software.amazon.awssdk:s3` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:dynamodb` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:lambda` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:kinesis` from 2.21.1 to 2.25.6 Updates `software.amazon.awssdk:cloudwatch` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:xray` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:cloudformation` from 2.24.10 to 2.25.6 Updates `software.amazon.awssdk:sts` from 2.24.10 to 2.25.6 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 3b2cf6df0..ef28cf2ab 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.24.10</aws.sdk.version> + <aws.sdk.version>2.25.6</aws.sdk.version> </properties> <dependencyManagement> <dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index d83a900c0..66aca544e 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -27,7 +27,7 @@ <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> - <version>2.24.10</version> + <version>2.25.6</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/pom.xml b/pom.xml index f9105b9d6..e30716325 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <log4j.version>2.22.1</log4j.version> <jackson.version>2.15.3</jackson.version> - <aws.sdk.version>2.24.10</aws.sdk.version> + <aws.sdk.version>2.25.6</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 6b264fec7dc2d8b88ee701e3b4973b5b0769b953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:04:23 +0100 Subject: [PATCH 0584/1008] build(deps): bump com.amazonaws:aws-lambda-java-events (#1597) Bumps [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs) from 3.11.2 to 3.11.4. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-events dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/app/pom.xml | 2 +- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-core/serverless/pom.xml | 2 +- examples/powertools-examples-core/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- pom.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ef28cf2ab..5e3403f4e 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.11.3</lambda.events.version> + <lambda.events.version>3.11.4</lambda.events.version> <aws.sdk.version>2.25.6</aws.sdk.version> </properties> <dependencyManagement> diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index 561aae686..4a8383925 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.4</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 2e95644b6..6ef2c0ecb 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.4</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 59820ffec..ef324056d 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.4</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 4784dc5e0..8781b70f4 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.2</version> + <version>3.11.4</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 64637be0e..e6d205827 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -52,7 +52,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.4</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index e5147acc4..5ef7d69a2 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.4</version> </dependency> </dependencies> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 4f2b8ffda..e63ecbc5a 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.4</version> </dependency> </dependencies> diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 66aca544e..01bbfd8d2 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -37,7 +37,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.11.4</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/pom.xml b/pom.xml index e30716325..e3f5dad3b 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.11.3</lambda.events.version> + <lambda.events.version>3.11.4</lambda.events.version> <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> From 190ad0a5e1cd82e533ee431c9ef382487a2c3f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:04:25 +0100 Subject: [PATCH 0585/1008] feat(build): remove java 8 support in v2 (#1606) * split GH actions for 2 versions * Update .github/workflows/publish.yml --- .github/workflows/pr_build.yml | 8 +-- .github/workflows/pr_build_v2.yml | 93 ++++++++++++++++++++++++++ .github/workflows/run-e2e-tests-v2.yml | 58 ++++++++++++++++ .github/workflows/run-e2e-tests.yml | 1 - 4 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/pr_build_v2.yml create mode 100644 .github/workflows/run-e2e-tests-v2.yml diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 54c2599c3..634a4ee0f 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -4,12 +4,10 @@ on: pull_request: branches: - main - - v2 paths: - 'powertools-batch/**' - 'powertools-cloudformation/**' - - 'powertools-core/**' # not in v2 - - 'powertools-common/**' # v2 only + - 'powertools-core/**' - 'powertools-e2e-tests/**' - 'powertools-idempotency/**' - 'powertools-large-messages/**' @@ -17,8 +15,8 @@ on: - 'powertools-metrics/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - - 'powertools-sqs/**' # not in v2 - - 'powertools-test-suite/**' # not in v2 + - 'powertools-sqs/**' + - 'powertools-test-suite/**' - 'powertools-tracing/**' - 'powertools-validation/**' - 'examples/**' diff --git a/.github/workflows/pr_build_v2.yml b/.github/workflows/pr_build_v2.yml new file mode 100644 index 000000000..3299dc720 --- /dev/null +++ b/.github/workflows/pr_build_v2.yml @@ -0,0 +1,93 @@ +name: Build + +on: + pull_request: + branches: + - v2 + paths: + - 'powertools-batch/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'examples/**' + - 'pom.xml' + - 'examples/pom.xml' + - '.github/workflows/**' + push: + branches: + - v2 + paths: + - 'powertools-batch/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'examples/**' + - 'pom.xml' + - 'examples/pom.xml' + - '.github/workflows/**' +jobs: + build-corretto: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + matrix: + java: [11, 17, 21] + name: Java ${{ matrix.java }} + env: + JAVA: ${{ matrix.java }} + AWS_REGION: eu-west-1 + permissions: + id-token: write # needed to interact with GitHub's OIDC Token endpoint. + contents: read + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: 'maven' + - name: Build with Maven + run: mvn -B install --file pom.xml + - name: Build Gradle Example - Java + working-directory: examples/powertools-examples-core/gradle + run: ./gradlew build + - name: Build Gradle Example - Kotlin + working-directory: examples/powertools-examples-core/kotlin + run: ./gradlew build + - name: Upload coverage to Codecov + uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.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,./powertools-large-messages/target/site/jacoco/jacoco.xml,./powertools-batch/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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + name: Upload artifact + with: + name: pr + path: pr/ diff --git a/.github/workflows/run-e2e-tests-v2.yml b/.github/workflows/run-e2e-tests-v2.yml new file mode 100644 index 000000000..255c89cfe --- /dev/null +++ b/.github/workflows/run-e2e-tests-v2.yml @@ -0,0 +1,58 @@ +name: Run end-to-end tests + +on: + workflow_dispatch: + + push: + branches: + - v2 + paths: # add other modules when there are under e2e tests + - 'powertools-e2e-tests/**' + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-common/**' + - 'powertools-idempotency/**' + - 'powertools-large-message/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-tracing/**' + - 'pom.xml' + - '.github/workflows/**' + + pull_request: + branches: + - v2 + paths: + - 'powertools-e2e-tests/**' + +jobs: + e2e: + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + java: [ 11, 17, 21 ] + name: End-to-end tests java${{ matrix.java }} + env: + AWS_DEFAULT_REGION: eu-west-1 + JAVA_VERSION: ${{ matrix.java }} + permissions: + id-token: write # needed to interact with GitHub's OIDC Token endpoint. + contents: read + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + 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/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 626268214..77cdea890 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -6,7 +6,6 @@ on: push: branches: - main - - v2 paths: # add other modules when there are under e2e tests - 'powertools-e2e-tests/**' - 'powertools-batch/**' From 4976a5bc4cf32a636c2ee3168990715775c78e65 Mon Sep 17 00:00:00 2001 From: Scott Gerring <gerrings@amazon.com> Date: Mon, 18 Mar 2024 10:34:15 +0100 Subject: [PATCH 0586/1008] fix: workflow paths for examples v2 builds --- .github/workflows/pr_build_v2.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_build_v2.yml b/.github/workflows/pr_build_v2.yml index 3299dc720..2079bcb6b 100644 --- a/.github/workflows/pr_build_v2.yml +++ b/.github/workflows/pr_build_v2.yml @@ -66,10 +66,10 @@ jobs: - name: Build with Maven run: mvn -B install --file pom.xml - name: Build Gradle Example - Java - working-directory: examples/powertools-examples-core/gradle + working-directory: examples/powertools-examples-core-utilities/gradle run: ./gradlew build - name: Build Gradle Example - Kotlin - working-directory: examples/powertools-examples-core/kotlin + working-directory: examples/powertools-examples-core-utilities/kotlin run: ./gradlew build - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 From 59f9ba4d96ebe0ae017ecf78f2b892f97315e09d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:09:36 +0100 Subject: [PATCH 0587/1008] build(deps): bump org.junit.jupiter:junit-jupiter-api (#1611) Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.3 to 5.10.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.3...r5.10.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 9a9351ea5..dc2bb86b0 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -69,7 +69,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> + <version>5.10.2</version> <scope>test</scope> </dependency> </dependencies> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 928e56e3a..fe97784d5 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -56,7 +56,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> + <version>5.10.2</version> <scope>test</scope> </dependency> <dependency> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1c39ee8a0..fd0278d78 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -59,7 +59,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.9.3</version> + <version>5.10.2</version> <scope>test</scope> </dependency> <dependency> From e4e2cd1a8676065b32bd20eb40444ae407c51405 Mon Sep 17 00:00:00 2001 From: Jeroen Reijn <j.reijn@gmail.com> Date: Thu, 21 Mar 2024 17:05:47 +0100 Subject: [PATCH 0588/1008] chore(v2): document use of aws-crt-client (#1092) (#1605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(v2): document use of aws-crt-client (#1092) * Update docs/FAQs.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> * chore(v2): improve documentation based on feedback (#1092) * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update docs/FAQs.md Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> * Update FAQs.md and set version --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> --- docs/FAQs.md | 100 ++++++++++++++++++++++++++++++- docs/utilities/batch.md | 26 ++++---- docs/utilities/large_messages.md | 85 +++++++++++++------------- docs/utilities/parameters.md | 1 + 4 files changed, 155 insertions(+), 57 deletions(-) diff --git a/docs/FAQs.md b/docs/FAQs.md index 99ef40905..88aa81c9f 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -6,7 +6,7 @@ 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. +Powertools 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. To enable in-place weaving feature you need to use following `aspectj-maven-plugin` configuration: @@ -29,7 +29,7 @@ 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`. +Powertools 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. To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` configuration: @@ -47,3 +47,99 @@ 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? + +Powertools uses 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 Powertools module, there is a different way to configure the SDK client. + +The following example shows how to use the Lambda Powertools Parameters module while leveraging the AWS CRT Client. + + ```java hl_lines="11-16 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.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 Lambda Powertools for 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`. \ No newline at end of file diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 6b38b438c..7b571bc6e 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -530,19 +530,19 @@ Handlers can be provided when building the batch processor and are available for For instance for DynamoDB: ```java -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); + 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); ``` !!! info diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index c0c1cd599..2f974c3b5 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -97,48 +97,49 @@ of amazon-sns-java-extended-client-lib. 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> -``` + + ```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" diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index f72c7704e..46c7e8071 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -544,6 +544,7 @@ To simplify the use of the library, you can chain all method calls before a get. .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 You can create your own custom parameter store provider by implementing a handful of classes: From 56e5f3980595d681a93120aad02a74c0516fcc80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:52:13 +0100 Subject: [PATCH 0589/1008] build(deps): bump jackson.version from 2.16.0 to 2.17.0 (#1615) Bumps `jackson.version` from 2.16.0 to 2.17.0. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.16.0 to 2.17.0 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.17.0 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.17.0) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.16.0 to 2.17.0 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aaf7ae002..7d271d8b5 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ <log4j.version>2.20.0</log4j.version> <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> - <jackson.version>2.16.0</jackson.version> + <jackson.version>2.17.0</jackson.version> <aws.sdk.version>2.25.6</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> From adbb7bfc0a2b32ba5e83b377d91897c89281c2ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:57:59 +0100 Subject: [PATCH 0590/1008] build(deps): bump co.elastic.logging:logback-ecs-encoder (#1617) Bumps [co.elastic.logging:logback-ecs-encoder](https://github.com/elastic/ecs-logging-java) from 1.5.0 to 1.6.0. - [Release notes](https://github.com/elastic/ecs-logging-java/releases) - [Commits](https://github.com/elastic/ecs-logging-java/compare/v1.5.0...v1.6.0) --- updated-dependencies: - dependency-name: co.elastic.logging:logback-ecs-encoder dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7d271d8b5..e0d9e631e 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ <junit.version>5.10.0</junit.version> <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> - <elastic.version>1.5.0</elastic.version> + <elastic.version>1.6.0</elastic.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. --> From daec77d18649ec3cb447f9b773aaea1d6b73c739 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:27:50 +0200 Subject: [PATCH 0591/1008] build(deps): bump aws.sdk.version from 2.25.6 to 2.25.21 (#1618) Bumps `aws.sdk.version` from 2.25.6 to 2.25.21. Updates `software.amazon.awssdk:bom` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:http-client-spi` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:url-connection-client` from 2.24.10 to 2.25.21 Updates `software.amazon.awssdk:dynamodb` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:s3` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:lambda` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:kinesis` from 2.24.10 to 2.25.21 Updates `software.amazon.awssdk:cloudwatch` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:xray` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:sqs` from 2.24.10 to 2.25.21 Updates `software.amazon.awssdk:cloudformation` from 2.25.6 to 2.25.21 Updates `software.amazon.awssdk:sts` from 2.25.6 to 2.25.21 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 107c8df90..077bab767 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.25.6</aws.sdk.version> + <aws.sdk.version>2.25.21</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index e0d9e631e..93d9deb8e 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.22.0</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.0</jackson.version> - <aws.sdk.version>2.25.6</aws.sdk.version> + <aws.sdk.version>2.25.21</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 7d40e6aacc58ff66aa1a5bdb212bf1ad05bfc97e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:56:43 +0200 Subject: [PATCH 0592/1008] build(deps): bump aws.sdk.version from 2.24.10 to 2.25.26 (#1623) Bumps `aws.sdk.version` from 2.24.10 to 2.25.26. Updates `software.amazon.awssdk:url-connection-client` from 2.24.10 to 2.25.26 Updates `software.amazon.awssdk:sdk-core` from 2.24.10 to 2.25.26 Updates `software.amazon.awssdk:kinesis` from 2.24.10 to 2.25.26 Updates `software.amazon.awssdk:sqs` from 2.24.10 to 2.25.26 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.24.10 to 2.25.26 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 70a4a6e3f..4f1eba3c1 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.24.10</sdk.version> + <sdk.version>2.25.26</sdk.version> </properties> <dependencies> From 1a1135d6b7581b41b23cb523a2e23434ca125dfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:51:36 +0200 Subject: [PATCH 0593/1008] build(deps): bump ch.qos.logback:logback-classic from 1.3.4 to 1.5.5 (#1626) Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.3.4 to 1.5.5. - [Commits](https://github.com/qos-ch/logback/compare/v_1.3.4...v_1.5.5) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-logging/powertools-logging-logback/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 99fff3ab9..3ab756334 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -29,7 +29,7 @@ <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <!-- TODO: use 1.4 if we remove JDK 1.8 support --> - <version>1.3.4</version> <!-- v1.3.x compatible with JDK 1.8, v1.4.x only compatible with JDK 11 --> + <version>1.5.5</version> <!-- v1.3.x compatible with JDK 1.8, v1.4.x only compatible with JDK 11 --> <scope>provided</scope> <!-- provided to let users change to 1.4.x when using JDK 11 --> <exclusions> <exclusion> From 36d82340148acbe3a21bb9c342fba1aea4f73300 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:53:41 +0200 Subject: [PATCH 0594/1008] build(deps): bump com.github.spotbugs:spotbugs-maven-plugin (#1627) Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.7.3.6 to 4.8.4.0. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.7.3.6...spotbugs-maven-plugin-4.8.4.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93d9deb8e..c7d75fa1b 100644 --- a/pom.xml +++ b/pom.xml @@ -517,7 +517,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.7.3.6</version> + <version>4.8.4.0</version> <executions> <execution> <id>test</id> From 4cd8f706269dec59f89d3b4a08caf1e05b7e0d2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:56:24 +0200 Subject: [PATCH 0595/1008] build(deps): bump org.junit:junit-bom from 5.10.0 to 5.10.2 (#1628) Bumps [org.junit:junit-bom](https://github.com/junit-team/junit5) from 5.10.0 to 5.10.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.0...r5.10.2) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c7d75fa1b..7c2b89c88 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ <maven-javadoc-plugin.version>3.6.3</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> + <junit.version>5.10.2</junit.version> <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> <elastic.version>1.6.0</elastic.version> From 34a94f31e136518eb0989b8850a1f888339e8d4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:59:35 +0200 Subject: [PATCH 0596/1008] build(deps): bump log4j.version from 2.20.0 to 2.23.1 (#1630) Bumps `log4j.version` from 2.20.0 to 2.23.1. Updates `org.apache.logging.log4j:log4j-core` from 2.20.0 to 2.23.1 Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.22.0 to 2.23.1 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.22.0 to 2.23.1 Updates `org.apache.logging.log4j:log4j-api` from 2.20.0 to 2.23.1 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.22.0 to 2.23.1 Updates `org.apache.logging.log4j:log4j-jcl` from 2.22.0 to 2.23.1 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index f964782e3..cc9643bda 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -11,7 +11,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.20.0</log4j.version> + <log4j.version>2.23.1</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/pom.xml b/pom.xml index 7c2b89c88..d12ac0c61 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <maven.deploy.plugin.version>3.1.1</maven.deploy.plugin.version> <log4j.version>2.20.0</log4j.version> - <log4j.version>2.22.0</log4j.version> + <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.0</jackson.version> <aws.sdk.version>2.25.21</aws.sdk.version> From 2d9364532bc9950fcb8829a3fc6000aa150f0ded Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:00:00 +0200 Subject: [PATCH 0597/1008] build(deps): bump aws.sdk.version from 2.25.21 to 2.25.35 (#1631) Bumps `aws.sdk.version` from 2.25.21 to 2.25.35. Updates `software.amazon.awssdk:bom` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:http-client-spi` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:url-connection-client` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:dynamodb` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:s3` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:lambda` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:kinesis` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:cloudwatch` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:xray` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:sqs` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:cloudformation` from 2.25.21 to 2.25.35 Updates `software.amazon.awssdk:sts` from 2.25.21 to 2.25.35 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 077bab767..d675a90c2 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.25.21</aws.sdk.version> + <aws.sdk.version>2.25.35</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index d12ac0c61..1d0ffe9a7 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.0</jackson.version> - <aws.sdk.version>2.25.21</aws.sdk.version> + <aws.sdk.version>2.25.35</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From e2b43efcc60a5af356967c8ca1cc96b302db4a28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:00:22 +0200 Subject: [PATCH 0598/1008] build(deps): bump dev.aspectj:aspectj-maven-plugin from 1.13.1 to 1.14 (#1632) Bumps [dev.aspectj:aspectj-maven-plugin](https://github.com/dev-aspectj/aspectj-maven-plugin) from 1.13.1 to 1.14. - [Release notes](https://github.com/dev-aspectj/aspectj-maven-plugin/releases) - [Commits](https://github.com/dev-aspectj/aspectj-maven-plugin/compare/aspectj-maven-plugin-1.13.1...aspectj-maven-plugin-1.14) --- updated-dependencies: - dependency-name: dev.aspectj:aspectj-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 4f1eba3c1..8aab869bb 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -75,7 +75,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d675a90c2..bbe849c1e 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -87,7 +87,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index cc9643bda..1ad3b9b68 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -66,7 +66,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index c9d90d281..3e495c960 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -52,7 +52,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 32cca9bb4..d899b40de 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -53,7 +53,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 94f79a5fd..66418888a 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -53,7 +53,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index dc2bb86b0..63640be57 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -79,7 +79,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index fe97784d5..e3e3ec9b2 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -78,7 +78,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 96afc3ea0..7858ee2a4 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -49,7 +49,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index fd0278d78..bf533dab9 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -75,7 +75,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/pom.xml b/pom.xml index 1d0ffe9a7..67dfc340a 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ <lambda.serial.version>1.1.2</lambda.serial.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> - <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> + <aspectj-maven-plugin.version>1.14</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> <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index df6154560..d76137678 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -88,7 +88,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 3ab756334..c43526dd4 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -87,7 +87,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.13.1</version> + <version>1.14</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From c7b2f7e30b6a08ce568e2ba6c26029a48ee3a4e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 14:00:58 +0200 Subject: [PATCH 0599/1008] build(deps): bump org.codehaus.mojo:exec-maven-plugin (#1645) Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.2.0...3.3.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 2dd61753a..e7d941ae8 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -25,7 +25,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.2.0</version> + <version>3.3.0</version> <configuration> <mainClass>cdk.CdkApp</mainClass> </configuration> From 16ad40335926e997f08cdfa857b85eb94b8c308d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:45:24 +0200 Subject: [PATCH 0600/1008] build(deps): bump org.apache.maven.plugins:maven-deploy-plugin (#1647) Bumps [org.apache.maven.plugins:maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.1.1...maven-deploy-plugin-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index f864c3c04..1f985a9cf 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -48,7 +48,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 8aab869bb..8cadd1393 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -136,7 +136,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index bbe849c1e..d7f372002 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -144,7 +144,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 1ad3b9b68..5ac2a89ac 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -132,7 +132,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index e7d941ae8..81bdbe185 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -34,7 +34,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 66418888a..e9dec2e34 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -118,7 +118,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 63640be57..f0f0628c9 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -176,7 +176,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index e3e3ec9b2..340bcbafb 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -108,7 +108,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 7858ee2a4..2f9bf77ef 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -41,7 +41,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index bf533dab9..f2f6e1f08 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -106,7 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/pom.xml b/pom.xml index 67dfc340a..4af6cdb32 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> - <maven.deploy.plugin.version>3.1.1</maven.deploy.plugin.version> + <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.20.0</log4j.version> <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> From d38995bf6a7cbc471302e8e6a32a6d55a2f0b4bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:48:34 +0200 Subject: [PATCH 0601/1008] build(deps): bump org.apache.maven.plugins:maven-shade-plugin (#1648) Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.0...maven-shade-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 8cadd1393..3d94982fd 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -109,7 +109,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.2</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d7f372002..0aa8fab83 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -117,7 +117,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 5ac2a89ac..f942fe1fa 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -104,7 +104,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 3e495c960..75821f851 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -90,7 +90,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index d899b40de..a83050b0a 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -91,7 +91,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index e9dec2e34..1918b63a9 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -91,7 +91,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index f0f0628c9..422a8ebb5 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -150,7 +150,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <executions> <execution> <phase>package</phase> From 04b9c159144f71ce19684a4ef7f9400ad83d744d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:25:32 +0200 Subject: [PATCH 0602/1008] build(deps): bump org.apache.maven.plugins:maven-jar-plugin (#1649) Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.3.0 to 3.4.1. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.3.0...maven-jar-plugin-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/powertools-idempotency-dynamodb/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index a8f69c468..c0a807ed8 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -79,7 +79,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>3.3.0</version> + <version>3.4.1</version> <configuration> <archive> <manifestEntries> From e347e41fe8849e100973d3b3472d7dce2332d159 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:32:17 +0200 Subject: [PATCH 0603/1008] build(deps): bump org.apache.maven.plugins:maven-compiler-plugin (#1651) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.11.0 to 3.13.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.11.0...maven-compiler-plugin-3.13.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 81bdbe185..100928618 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -16,7 +16,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.11.0</version> + <version>3.13.0</version> <configuration> <source>11</source> <target>11</target> diff --git a/pom.xml b/pom.xml index 4af6cdb32..b24608f06 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ <lambda.core.version>1.2.3</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> + <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 20679d64f..c3bfca0df 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -192,7 +192,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.11.0</version> + <version>3.13.0</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From a5c22c46b6ca48fc31458920de25be6341709982 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:33:15 +0000 Subject: [PATCH 0604/1008] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.10 to 0.8.12 (#1656) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.10 to 0.8.12. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.10...v0.8.12) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b24608f06..8055c41a8 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ <aspectj.version>1.9.7</aspectj.version> <aspectj-maven-plugin.version>1.14</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.12</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.6.3</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> From 12a2f44d3cf1b7e0fbba3937b9e18363c8bde663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:47:22 +0200 Subject: [PATCH 0605/1008] Update run-e2e-tests.yml remove v2 --- .github/workflows/run-e2e-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index b358fe355..13c84befe 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -24,7 +24,6 @@ on: pull_request: branches: - main - - v2 paths: - 'powertools-e2e-tests/**' @@ -63,4 +62,4 @@ jobs: if: ${{ matrix.java != '8' }} # If not 8 don't exclude the examples directory run: mvn -DskipTests install --file pom.xml - name: Run e2e test with Maven - run: mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml \ No newline at end of file + run: mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml From 471c3a61eba48e8731ba513fd1748b530f198cdb Mon Sep 17 00:00:00 2001 From: Scott Gerring <scottgerring@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:57:34 +0200 Subject: [PATCH 0606/1008] feat(v2): publish snapshots (#1655) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial commit * Add distributionManagement back * Switch to environment variables * Enforcer should work * Re-enable GPG * Change user/password for publish * Missing config? * Skip SAM example for deploy * Another missing one * Publish preview docs * Update docs accordingly * Exclude intentional constructor throw from spotbugs * More new spotbugs? * Block new spotbugs rules * Remove PR push triggers * Again but properly --------- Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- .github/workflows/docs-v2-snapshot.yml | 40 +++++++++++++++++++ .github/workflows/publish-v2-snapshot.yml | 29 ++++++++++++++ .github/workflows/publish.yml | 4 +- docs/index.md | 8 +++- .../sam/pom.xml | 9 +++++ .../serverless/pom.xml | 9 +++++ mkdocs.yml | 6 +-- pom.xml | 29 ++++++++++++++ .../dynamodb-local-metadata.json | 1 + spotbugs-exclude.xml | 16 ++++++++ 10 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/docs-v2-snapshot.yml create mode 100644 .github/workflows/publish-v2-snapshot.yml create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json diff --git a/.github/workflows/docs-v2-snapshot.yml b/.github/workflows/docs-v2-snapshot.yml new file mode 100644 index 000000000..55803c737 --- /dev/null +++ b/.github/workflows/docs-v2-snapshot.yml @@ -0,0 +1,40 @@ +name: Docs +on: + push: + branches: + - v2 + workflow_dispatch: {} + +permissions: + id-token: write + contents: write + pages: write + +jobs: + docs: + runs-on: ubuntu-latest + environment: Docs + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Set up Python + uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 + 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/preview/ diff --git a/.github/workflows/publish-v2-snapshot.yml b/.github/workflows/publish-v2-snapshot.yml new file mode 100644 index 000000000..d5a683261 --- /dev/null +++ b/.github/workflows/publish-v2-snapshot.yml @@ -0,0 +1,29 @@ +name: Publish v2 +on: + push: + branches: + - v2 + workflow_dispatch: {} +jobs: + publish: + runs-on: ubuntu-latest + environment: snapshot + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Set up Maven Central Repository + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: 11 + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + # TODO: use environments https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment + 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: Publish package + run: mvn -Prelease clean validate deploy -DskipTests # We use validate here to run maven enforcer, to make sure we are only publishing SNAPSHOT builds + env: + MAVEN_USERNAME: ${{ secrets.SNAPSHOT_PUBLISH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.SNAPSHOT_PUBLISH_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 03f04e0f4..d2d47b308 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,8 +27,8 @@ jobs: - name: Publish package run: mvn -Prelease clean deploy -DskipTests env: - MAVEN_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} + MAVEN_USERNAME: ${{ secrets.SNAPSHOT_PUBLISH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.SNAPSHOT_PUBLISH_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Close issues related to this release uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 diff --git a/docs/index.md b/docs/index.md index 06c9beb6d..1c5dfac85 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,13 @@ 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) +![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/publish-v2-snapshot.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Faws.oss.sonatype.org%2Fcontent%2Frepositories%2Fsnapshots%2Fsoftware%2Famazon%2Flambda%2Fpowertools-parent%2Fmaven-metadata.xml +) + +???+ warning + You are browsing the documentation for Powertools for AWS Lambda (Java) - v2. This is a snapshot release and not stable! + Check out our stable [v1](https://docs.powertools.aws.dev/lambda/java/) documentation if this is not what you wanted. + The v2 maven snapshot repository can be found [here](https://aws.oss.sonatype.org/content/repositories/snapshots/software/amazon/lambda/) . 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. diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 75821f851..86fa52425 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -113,6 +113,15 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> </project> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index a83050b0a..0b5b06152 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -114,6 +114,15 @@ </dependency> </dependencies> </plugin> + <!-- Don't deploy the example --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> </project> diff --git a/mkdocs.yml b/mkdocs.yml index b7f793e18..aa7b0e314 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ -site_name: Powertools for AWS Lambda (Java) -site_description: Powertools for AWS Lambda (Java) +site_name: Powertools for AWS Lambda (Java) Preview +site_description: Powertools for AWS Lambda (Java) Preview site_author: Amazon Web Services site_url: https://docs.powertools.aws.dev/lambda-java/ nav: @@ -85,7 +85,7 @@ extra_javascript: extra: powertools: - version: 2.0.0 # to update after each release (we do not want snapshot version here) + version: 2.0.0-SNAPSHOT repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index 8055c41a8..2f4e327e6 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,13 @@ <project.rootdir>${maven.multiModuleProjectDirectory}</project.rootdir> </properties> + <distributionManagement> + <snapshotRepository> + <id>ossrh</id> + <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> + </snapshotRepository> + </distributionManagement> + <dependencyManagement> <dependencies> <dependency> @@ -457,6 +464,28 @@ <id>release</id> <build> <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <version>3.5.0</version> + <executions> + <execution> + <id>enforce-snapshot-versions</id> + <phase>validate</phase> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireSnapshotVersion> + <message>Release build should not have snapshot dependencies!</message> + </requireSnapshotVersion> + </rules> + <fail>true</fail> + </configuration> + </execution> + </executions> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json b/powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json new file mode 100644 index 000000000..c76c2c76d --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json @@ -0,0 +1 @@ +{"installationId":"e43b8515-8484-485c-8315-bead4568972b","telemetryEnabled":"true"} \ No newline at end of file diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index a4eb3756f..ee44f7b4d 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -8,6 +8,22 @@ https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html --> <FindBugsFilter> + <!-- 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="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> From 31124c46af235c9f3fa2174f5c6ac4439adf66e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:52:58 +0200 Subject: [PATCH 0607/1008] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin (#1634) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f4e327e6..ba2ef5dba 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.6.3</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> + <maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version> <junit.version>5.10.2</junit.version> <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> From 598a20746d97807145714c775aac7794c0b70481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:11:00 +0200 Subject: [PATCH 0608/1008] build(deps): bump com.amazonaws:aws-lambda-java-serialization (#1636) Bumps [com.amazonaws:aws-lambda-java-serialization](https://github.com/aws/aws-lambda-java-libs) from 1.1.2 to 1.1.5. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ba2ef5dba..d96c51d2f 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> - <lambda.serial.version>1.1.2</lambda.serial.version> + <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> From 0d8e2b1166c27d8fcabff80502d8013eae564577 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:35:30 +0200 Subject: [PATCH 0609/1008] build(deps): bump commons-io:commons-io from 2.15.1 to 2.16.1 (#1635) Bumps commons-io:commons-io from 2.15.1 to 2.16.1. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index c3bfca0df..f4aaf3715 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -103,7 +103,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.15.1</version> + <version>2.16.1</version> </dependency> <dependency> From 5b370d6a778b2c1e12584385cdda395856664acd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:05:46 +0200 Subject: [PATCH 0610/1008] build(deps-dev): bump com.amazonaws:amazon-sqs-java-extended-client-lib (#1650) Bumps [com.amazonaws:amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) from 2.0.4 to 2.1.0. --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index f4aaf3715..6eb67d4d5 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -84,7 +84,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - <version>2.0.4</version> + <version>2.1.0</version> <scope>test</scope> </dependency> From 8a1092185e546af3d33f3afb2c6d9a51ed50f4e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:32:16 +0000 Subject: [PATCH 0611/1008] build(deps): bump aws.sdk.version from 2.25.26 to 2.26.7 (#1660) Bumps `aws.sdk.version` from 2.25.26 to 2.26.7. Updates `software.amazon.awssdk:url-connection-client` from 2.25.26 to 2.26.7 Updates `software.amazon.awssdk:sdk-core` from 2.25.26 to 2.26.7 Updates `software.amazon.awssdk:kinesis` from 2.25.26 to 2.26.7 Updates `software.amazon.awssdk:sqs` from 2.25.26 to 2.26.7 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.25.26 to 2.26.7 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 3d94982fd..b9a523d04 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.25.26</sdk.version> + <sdk.version>2.26.7</sdk.version> </properties> <dependencies> From 814b3fbae9b3482b61f8a0ae8bacb0c44416bd6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:33:16 +0000 Subject: [PATCH 0612/1008] build(deps): bump software.amazon.payloadoffloading:payloadoffloading-common (#1661) Bumps [software.amazon.payloadoffloading:payloadoffloading-common](https://github.com/awslabs/payload-offloading-java-common-lib-for-aws) from 2.1.3 to 2.2.0. - [Release notes](https://github.com/awslabs/payload-offloading-java-common-lib-for-aws/releases) - [Commits](https://github.com/awslabs/payload-offloading-java-common-lib-for-aws/compare/v2.1.3...v2.2.0) --- updated-dependencies: - dependency-name: software.amazon.payloadoffloading:payloadoffloading-common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d96c51d2f..61946007a 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ <jackson.version>2.17.0</jackson.version> <aws.sdk.version>2.25.35</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> - <payloadoffloading-common.version>2.1.3</payloadoffloading-common.version> + <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.2</lambda.events.version> From 1074aab205149b89423e82e1e6738690357c9d98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:34:13 +0000 Subject: [PATCH 0613/1008] build(deps): bump jackson.version from 2.17.0 to 2.17.1 (#1664) Bumps `jackson.version` from 2.17.0 to 2.17.1. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.17.0 to 2.17.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.17.0 to 2.17.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.17.0...jackson-core-2.17.1) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.17.0 to 2.17.1 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61946007a..0b51e0086 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ <log4j.version>2.20.0</log4j.version> <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> - <jackson.version>2.17.0</jackson.version> + <jackson.version>2.17.1</jackson.version> <aws.sdk.version>2.25.35</aws.sdk.version> <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> From 42b8325b893d2bfe5c413388ed427e76c64f3d29 Mon Sep 17 00:00:00 2001 From: jdoherty <jdoherty07@gmail.com> Date: Mon, 24 Jun 2024 12:37:04 +0100 Subject: [PATCH 0614/1008] feat: upgraded embedded metrics library for high resolution metrics (#1550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * upgraded embedded metrics library and associated tests * Removing LambdaJsonLayout from logging in examples (#1545) * updated formatting and changed assertion for sonar * fix example * do not build on java 8 for v2 (oopsy) * spotbugs fix * update documentation --------- Co-authored-by: ritigupt <102658810+ritigupt@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com> Co-authored-by: Jerome Van Der Linden <jeromevdl@gmail.com> Co-authored-by: Jérôme Van Der Linden <117538+jeromevdl@users.noreply.github.com> --- docs/core/metrics.md | 112 ++++++------------ .../sam/src/main/java/helloworld/App.java | 3 + pom.xml | 2 +- .../lambda/powertools/e2e/Function.java | 14 ++- .../amazon/lambda/powertools/e2e/Input.java | 10 ++ .../amazon/lambda/powertools/MetricsE2ET.java | 39 +++++- .../powertools/metrics/MetricsLoggerTest.java | 11 ++ ...rtoolsMetricsTooManyDimensionsHandler.java | 11 +- .../internal/LambdaMetricsAspectTest.java | 6 +- spotbugs-exclude.xml | 4 + 10 files changed, 123 insertions(+), 89 deletions(-) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index e06ab6d10..165c26e1c 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -28,9 +28,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> @@ -75,52 +73,7 @@ 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" plugins { @@ -140,34 +93,14 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar targetCompatibility = 11 ``` -=== "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-metrics:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - ## Getting started Metric has two global settings that will be used across all metrics emitted: -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` +| 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` | !!! tip "Use your application or main service as the metric namespace to easily group all metrics" @@ -198,7 +131,7 @@ Setting | Description | Environment variable | Constructor parameter @Override @Metrics(namespace = "ExampleApplication", service = "booking") public Object handleRequest(Object input, Context context) { - ... + // ... } } ``` @@ -224,7 +157,7 @@ You can create metrics using `putMetric`, and manually create dimensions for all public Object handleRequest(Object input, Context context) { metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); - ... + // ... } } ``` @@ -234,6 +167,35 @@ You can create metrics using `putMetric`, and manually create dimensions for all !!! 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. + +### Adding high-resolution metrics + +You can create [high-resolution metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics) +passing a `storageResolution` to the `putMetric` method: + +=== "HigResMetricsHandler.java" + + ```java hl_lines="3 13" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; + import software.amazon.cloudwatchlogs.emf.model.StorageResolution; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); + + @Override + @Metrics(namespace = "ExampleApplication", service = "booking") + public Object handleRequest(Object input, Context context) { + // ... + metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT, StorageResolution.HIGH); + } + } + ``` + +!!! 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. + ### Flushing metrics The `@Metrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, 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 index b1a701b8f..e7c410042 100644 --- 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 @@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory; import org.slf4j.MDC; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.StorageResolution; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.metrics.Metrics; @@ -64,6 +65,8 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); }); + metricsLogger().putMetric("CustomMetric3", 1, Unit.COUNT, StorageResolution.HIGH); + MDC.put("test", "willBeLogged"); APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() diff --git a/pom.xml b/pom.xml index 0b51e0086..168243622 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ <maven-source-plugin.version>3.3.0</maven-source-plugin.version> <maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version> <junit.version>5.10.2</junit.version> - <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> + <aws-embedded-metrics.version>4.1.2</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> <elastic.version>1.6.0</elastic.version> 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..a86e515f7 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 @@ -18,9 +18,15 @@ 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.StorageResolution; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.metrics.MetricsUtils; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.time.Instant; + public class Function implements RequestHandler<Input, String> { @@ -29,11 +35,17 @@ public class Function implements RequestHandler<Input, String> { @Metrics(captureColdStart = true) public String handleRequest(Input input, Context context) { + Instant currentTimeTruncatedPlusThirty = + LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).toInstant(ZoneOffset.UTC).plusSeconds(30); + metricsLogger.setTimestamp(currentTimeTruncatedPlusThirty); + DimensionSet dimensionSet = new DimensionSet(); input.getDimensions().forEach((key, value) -> dimensionSet.addDimension(key, value)); metricsLogger.putDimensions(dimensionSet); - input.getMetrics().forEach((key, value) -> metricsLogger.putMetric(key, value, Unit.COUNT)); + input.getMetrics().forEach((key, value) -> metricsLogger.putMetric(key, value, Unit.COUNT, + input.getHighResolution().equalsIgnoreCase("true") ? StorageResolution.HIGH : + StorageResolution.STANDARD)); return "OK"; } 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..1328ded77 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,6 +21,8 @@ public class Input { private Map<String, String> dimensions; + private String highResolution; + public Input() { } @@ -32,6 +34,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/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java index 80673b995..235255dff 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,10 @@ import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT; import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.List; import java.util.Map; @@ -66,12 +70,20 @@ public static void tearDown() { @Test public void test_recordMetrics() { // GIVEN + + Instant currentTimeTruncatedToMinutes = + LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).toInstant(ZoneOffset.UTC); + String event1 = - "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; + "{ \"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 = @@ -84,18 +96,35 @@ public void test_recordMetrics() { List<Double> orderMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "orders", Collections.singletonMap("Environment", "test")); - assertThat(orderMetrics.get(0)).isEqualTo(1); + assertThat(orderMetrics.get(0)).isEqualTo(2); List<Double> productMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "products", Collections.singletonMap("Environment", "test")); - assertThat(productMetrics.get(0)).isEqualTo(4); + + // 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(1); + 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(4); + 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-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java index 0402e3cc5..26ae41a00 100644 --- 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 @@ -25,6 +25,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; import org.junit.jupiter.api.AfterEach; @@ -35,6 +37,7 @@ 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.StorageResolution; import software.amazon.cloudwatchlogs.emf.model.Unit; class MetricsLoggerTest { @@ -245,6 +248,7 @@ private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { { metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); metricsLogger.putMetric("Metric1", 1, Unit.COUNT); + metricsLogger.putMetric("Metric2", 1, Unit.COUNT, StorageResolution.HIGH); }); assertThat(out.toString()) @@ -263,6 +267,13 @@ private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { assertThat(aws.get("CloudWatchMetrics")) .asString() .contains("Namespace=GlobalName"); + + ArrayList cloudWatchMetrics = (ArrayList) aws.get("CloudWatchMetrics"); + LinkedHashMap<String, Object> values = + (java.util.LinkedHashMap<String, Object>) cloudWatchMetrics.get(0); + ArrayList metricArray = (ArrayList) values.get("Metrics"); + LinkedHashMap<String, Object> metricValues = (LinkedHashMap<String, Object>) metricArray.get(1); + assertThat(metricValues).containsEntry("StorageResolution", 1); }); } } 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 index bc8a6e949..fd406b9cd 100644 --- 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 @@ -26,13 +26,14 @@ public class PowertoolsMetricsTooManyDimensionsHandler implements RequestHandler<Object, Object> { @Override - @Metrics + @Metrics(namespace = "ExampleApplication",service = "booking") 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)); + DimensionSet dimensionSet = new DimensionSet(); + for (int i = 0; i < 35; i++) { + dimensionSet.addDimension("Dimension" + i, "value" + i); + } + metricsLogger.setDimensions(dimensionSet); return null; } 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 81e10ed22..d27af1fdf 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 @@ -40,6 +40,7 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; +import software.amazon.cloudwatchlogs.emf.exception.DimensionSetExceededException; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; @@ -389,9 +390,10 @@ public void exceptionWhenTooManyDimensionsSet() { requestHandler = new PowertoolsMetricsTooManyDimensionsHandler(); - assertThatExceptionOfType(ValidationException.class) + assertThatExceptionOfType(DimensionSetExceededException.class) .isThrownBy(() -> requestHandler.handleRequest("input", context)) - .withMessage("Number of Dimensions must be in range of 0-9. Actual size: 14."); + .withMessage( + "Maximum number of dimensions allowed are 30. Account for default dimensions if not using setDimensions."); } } diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index ee44f7b4d..e959204ad 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -233,6 +233,10 @@ <Class name="software.amazon.lambda.powertools.sqs.SqsUtils"/> <Method name="s3Client"/> </And> + <And> + <Class name="software.amazon.lambda.powertools.metrics.MetricsUtils"/> + <Method name="metricsLogger"/> + </And> </Or> </Match> <!--False positive https://github.com/spotbugs/spotbugs/issues/1539--> From 1317da4ad623dec472fbadb0f22486980356ee1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:38:26 +0000 Subject: [PATCH 0615/1008] build(deps): bump aws.xray.recorder.version from 2.15.1 to 2.16.0 (#1662) Bumps `aws.xray.recorder.version` from 2.15.1 to 2.16.0. Updates `com.amazonaws:aws-xray-recorder-sdk-core` from 2.15.1 to 2.16.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.1...v2.16.0) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core` from 2.15.1 to 2.16.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.1...v2.16.0) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2` from 2.15.1 to 2.16.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.1...v2.16.0) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.15.1 to 2.16.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.15.1...v2.16.0) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 168243622..acfe00fd1 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.1</jackson.version> <aws.sdk.version>2.25.35</aws.sdk.version> - <aws.xray.recorder.version>2.15.1</aws.xray.recorder.version> + <aws.xray.recorder.version>2.16.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.3</lambda.core.version> From adbd75069ca01d8e13ec692d4f648754e21fd999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:17:25 +0000 Subject: [PATCH 0616/1008] chore(v2): remove java 1.8 relics from the code (#1659) * remove java 1.8 relics --- .github/workflows/pr_build.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/run-e2e-tests.yml | 7 +- .github/workflows/spotbugs.yml | 2 +- README.md | 88 +------------ docs/core/tracing.md | 72 +---------- docs/index.md | 120 ++---------------- docs/utilities/idempotency.md | 70 +--------- docs/utilities/large_messages.md | 71 +---------- docs/utilities/parameters.md | 80 +----------- docs/utilities/validation.md | 71 +---------- .../kotlin/build.gradle.kts | 2 +- .../powertools-examples-validation/pom.xml | 24 +--- pom.xml | 5 +- powertools-e2e-tests/handlers/pom.xml | 8 +- powertools-e2e-tests/pom.xml | 4 +- .../powertools/testutils/Infrastructure.java | 4 +- .../powertools/testutils/JavaRuntime.java | 2 - .../persistence/BasePersistenceStore.java | 24 ++-- .../powertools-logging-logback/pom.xml | 4 +- 20 files changed, 59 insertions(+), 603 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 634a4ee0f..1ff2dc48b 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -51,7 +51,7 @@ jobs: strategy: max-parallel: 5 matrix: - java: [8, 11, 17, 21] + java: [11, 17, 21] name: Java ${{ matrix.java }} env: JAVA: ${{ matrix.java }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d2d47b308..68c4d2e52 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - java-version: 8 + java-version: 11 server-id: ossrh server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 13c84befe..86d66156b 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -33,7 +33,7 @@ jobs: strategy: max-parallel: 4 matrix: - java: [ 8, 11, 17, 21 ] + java: [ 11, 17, 21 ] name: End-to-end tests java${{ matrix.java }} env: AWS_DEFAULT_REGION: eu-west-1 @@ -54,12 +54,7 @@ jobs: with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} - - - name: Build with Maven Java 8 - if: ${{ matrix.java == '8' }} # If 8 exclude the examples directory - run: mvn -DskipTests install --file pom.xml -pl '!software.amazon.lambda.examples:powertools-examples-idempotency,!software.amazon.lambda.examples:powertools-examples-batch,!software.amazon.lambda.examples:powertools-examples-cloudformation,!software.amazon.lambda.examples:powertools-examples-core-utilities-cdk,!software.amazon.lambda.examples:powertools-examples-core-utilities-sam,!software.amazon.lambda.examples:powertools-examples-core-utilities-serverless,!software.amazon.lambda.examples:powertools-examples-core-utilities-terraform,!software.amazon.lambda.examples:powertools-examples-parameters,!software.amazon.lambda.examples:powertools-examples-serialization,!software.amazon.lambda.examples:powertools-examples-validation,!software.amazon.lambda.examples:cdk,!software.amazon.lambda:powertools-examples' - name: Build with Maven - if: ${{ matrix.java != '8' }} # If not 8 don't exclude the examples directory run: mvn -DskipTests install --file pom.xml - name: Run e2e test with Maven run: mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index f0d50d8c5..a75cba8f1 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java JDK 1.8 + - name: Setup java JDK 11 uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' diff --git a/README.md b/README.md index 83b026d15..bf04b54b1 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your </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> @@ -52,9 +52,9 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam <artifactId>aspectj-maven-plugin</artifactId> <version>1.13.1</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> @@ -85,51 +85,7 @@ 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 @@ -160,38 +116,9 @@ 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' - } - - // the freefair aspect plugins targets gradle 7.6.1 - // https://docs.freefair.io/gradle-plugins/6.6.3/reference/ - wrapper { - gradleVersion = "7.6.1" - } - - 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 -``` -</details> ### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the +Powertools for AWS Lambda (Java) supports all Java version from 11 up to 21 as well as 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 good version of `aspectjrt` to your dependencies based on the JDK used for building your function: @@ -209,7 +136,6 @@ You may need to add the good version of `aspectjrt` to your dependencies based o | JDK version | aspectj version | |-------------|-----------------| -| `1.8` | `1.9.7` | | `11-17` | `1.9.20.1` | | `21` | `1.9.21` | diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 17e81b867..4cb06fc29 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -17,9 +17,7 @@ a provides functionality to reduce the overhead of performing common tracing tas ## 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> @@ -64,52 +62,7 @@ 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+" +=== "Gradle" ```groovy hl_lines="3 11" plugins { @@ -129,27 +82,6 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl targetCompatibility = 11 ``` -=== "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-tracing:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - - ## 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. diff --git a/docs/index.md b/docs/index.md index 1c5dfac85..9092adc09 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,9 +86,7 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo * [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> @@ -151,70 +149,7 @@ 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 @@ -243,36 +178,6 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl targetCompatibility = 11 ``` -=== "Gradle Java 1.8" - - ```groovy - - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - // the freefair aspect plugins targets gradle 7.6.1 - // https://docs.freefair.io/gradle-plugins/6.6.3/reference/ - wrapper { - gradleVersion = "7.6.1" - } - - - 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 - ``` - ???+ 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 @@ -281,7 +186,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl while `dev.aspectj:aspectj-maven-plugin` is based on AspectJ 1.9.8, compiled for Java 11+. ### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the +Powertools for AWS Lambda (Java) supports all Java version from 11 up to 21 as well as the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). For the following modules, Powertools for AWS Lambda (Java) leverages the **aspectj** library to provide annotations: @@ -308,7 +213,6 @@ Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj | JDK version | aspectj version | |-------------|-----------------| -| `1.8` | `1.9.7` | | `11-17` | `1.9.20.1` | | `21` | `1.9.21` | @@ -317,13 +221,13 @@ Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj !!! 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_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) | +| 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_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/utilities/idempotency.md b/docs/utilities/idempotency.md index f4defbdfd..240752a55 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -26,9 +26,8 @@ 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" <dependencies> @@ -73,52 +72,7 @@ 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-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 --> - <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-dynamodb</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" plugins { @@ -138,26 +92,6 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl 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-dynamodb:{{ 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. diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index 2f974c3b5..29244de98 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -94,9 +94,7 @@ of 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+" +=== "Maven" ```xml hl_lines="3-7 16 18 24-27" <dependencies> @@ -141,52 +139,7 @@ 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-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>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> - </plugin> - ... - </plugins> - </build> - ``` - -=== "Gradle Java 11+" +=== "Gradle" ```groovy hl_lines="3 11" plugins { @@ -206,26 +159,6 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl 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 diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 46c7e8071..ab9c04c64 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -26,10 +26,9 @@ package: You can easily mix and match parameter providers within the same project for different needs. -Depending on which Java version you are using, you configuration will differ. Note that you must also provide -the concrete parameters module you want to use below - see the TODOs! +Note that you must provide the concrete parameters module you want to use below - see the TODOs! -=== "Maven Java 11+" +=== "Maven" ```xml hl_lines="4-12 17 24 30-34" <dependencies> @@ -81,59 +80,7 @@ the concrete parameters module you want to use below - see the TODOs! </build> ``` -=== "Maven Java 1.8" - - ```xml hl_lines="4-12 17 24 30-34" - <dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - - <!-- 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 --> - <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> - <!-- 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-secrets</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 12" plugins { @@ -154,27 +101,6 @@ the concrete parameters module you want to use below - see the TODOs! targetCompatibility = 11 // or higher ``` -=== "Gradle Java 1.8" - - ```groovy hl_lines="3 11 12" - plugins { - id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3' - } - - repositories { - mavenCentral() - } - - // TODO! Provide an aspectLibrary for each of the parameters module(s) you want to use here - dependencies { - aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - **IAM Permissions** This utility requires additional permissions to work as expected. See the table below: diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index dfd97e0d4..cc1c71bda 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -12,9 +12,8 @@ This utility provides JSON Schema validation for payloads held within events and * 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> ... @@ -58,52 +57,7 @@ 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-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+" +=== "Gradle" ```groovy hl_lines="3 11" plugins { @@ -123,27 +77,6 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl 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-validation:{{ powertools.version }}' - } - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - ``` - - ## Validating events You can validate inbound and outbound events using `@Validation` annotation. diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index b37ef6d31..3c84ce7f4 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { aspect("software.amazon.lambda:powertools-tracing:2.0.0-SNAPSHOT") aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0-SNAPSHOT") aspect("software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10") + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } kotlin { diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index f2f6e1f08..d1973f3df 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -22,8 +22,8 @@ <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.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> </properties> @@ -113,24 +113,4 @@ </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> - </profile> - </profiles> </project> diff --git a/pom.xml b/pom.xml index acfe00fd1..aeb8ba6d4 100644 --- a/pom.xml +++ b/pom.xml @@ -64,8 +64,8 @@ </modules> <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> + <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.20.0</log4j.version> <log4j.version>2.23.1</log4j.version> @@ -661,7 +661,6 @@ <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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 1bf26f871..b55cf436a 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -12,8 +12,8 @@ <properties> <lambda.powertools.version>2.0.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> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <lambda.java.core>1.2.2</lambda.java.core> <lambda.java.serialization>1.1.2</lambda.java.serialization> @@ -192,9 +192,9 @@ <profiles> <!-- https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md --> <profile> - <id>jdk8to16</id> + <id>jdk11to16</id> <activation> - <jdk>[1.8,16]</jdk> + <jdk>[11,16]</jdk> </activation> <properties> <aspectj.version>1.9.7</aspectj.version> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 6eb67d4d5..5171bb418 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -28,8 +28,8 @@ <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> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.3.0</constructs.version> <cdk.version>2.130.0</cdk.version> </properties> 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 28a0f2bb4..bce4bbf98 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 @@ -517,9 +517,7 @@ private JavaRuntime mapRuntimeVersion(String environmentVariableName) { if (javaVersion == null) { throw new IllegalArgumentException(environmentVariableName + " is not set"); } - if (javaVersion.startsWith("8")) { - ret = JavaRuntime.JAVA8AL2; - } else if (javaVersion.startsWith("11")) { + if (javaVersion.startsWith("11")) { ret = JavaRuntime.JAVA11; } else if (javaVersion.startsWith("17")) { ret = JavaRuntime.JAVA17; 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 c75682949..53d35e86d 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,8 +17,6 @@ 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"), JAVA21("java21", Runtime.JAVA_21, "21"); diff --git a/powertools-idempotency/powertools-idempotency-core/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 index bafbcbd42..8b08434ba 100644 --- a/powertools-idempotency/powertools-idempotency-core/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,20 +14,12 @@ package software.amazon.lambda.powertools.idempotency.persistence; +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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -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.exceptions.IdempotencyKeyException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; -import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; -import software.amazon.lambda.powertools.utilities.JsonConfig; - import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -41,8 +33,15 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; - -import static software.amazon.lambda.powertools.common.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.utilities.JsonConfig; /** * Persistence layer that will store the idempotency result. @@ -315,6 +314,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 { diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index c43526dd4..11fc85b72 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -28,9 +28,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <!-- TODO: use 1.4 if we remove JDK 1.8 support --> - <version>1.5.5</version> <!-- v1.3.x compatible with JDK 1.8, v1.4.x only compatible with JDK 11 --> - <scope>provided</scope> <!-- provided to let users change to 1.4.x when using JDK 11 --> + <version>1.5.5</version> <exclusions> <exclusion> <groupId>com.sun.mail</groupId> From 71109d90fb5297cbea3c693c26ad764899e097c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:04:39 +0000 Subject: [PATCH 0617/1008] build(deps): bump aws.sdk.version from 2.25.35 to 2.26.12 (#1681) Bumps `aws.sdk.version` from 2.25.35 to 2.26.12. Updates `software.amazon.awssdk:url-connection-client` from 2.25.35 to 2.26.12 Updates `software.amazon.awssdk:sdk-core` from 2.26.7 to 2.26.12 Updates `software.amazon.awssdk:kinesis` from 2.25.35 to 2.26.12 Updates `software.amazon.awssdk:sqs` from 2.25.35 to 2.26.12 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.26.7 to 2.26.12 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index b9a523d04..207040476 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.26.7</sdk.version> + <sdk.version>2.26.12</sdk.version> </properties> <dependencies> From 2285374225c108f8a5336038b468182d81bd7986 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:05:22 +0000 Subject: [PATCH 0618/1008] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin (#1683) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.6.3 to 3.7.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.3...maven-javadoc-plugin-3.7.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aeb8ba6d4..e685d056c 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> - <maven-javadoc-plugin.version>3.6.3</maven-javadoc-plugin.version> + <maven-javadoc-plugin.version>3.7.0</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> <maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version> <junit.version>5.10.2</junit.version> From 57f3efff187e8da8c487602d8cef15d6228a2108 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:10:38 +0000 Subject: [PATCH 0619/1008] build(deps): bump org.sonatype.plugins:nexus-staging-maven-plugin (#1682) Bumps org.sonatype.plugins:nexus-staging-maven-plugin from 1.6.13 to 1.7.0. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e685d056c..4ba84d4fc 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> - <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version> + <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.7.0</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> <maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version> From b4f9ee932ca9794583c59d178c59b13db314215c Mon Sep 17 00:00:00 2001 From: Justin Tay <49700559+justin-tay@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:54:48 +0800 Subject: [PATCH 0620/1008] build(deps): bump com.networknt:json-schema-validator from 1.0.87 to 1.4.3 (#1674) --- docs/utilities/validation.md | 7 ++++-- .../lambda/powertools/ValidationALBE2ET.java | 4 ++-- .../powertools/ValidationApiGWE2ET.java | 4 ++-- powertools-validation/pom.xml | 2 +- .../validation/ValidationConfig.java | 8 ++++++- .../validation/ValidationUtils.java | 23 ++++++++----------- .../validation/ValidationUtilsTest.java | 4 ++-- .../src/test/resources/schema_v7.json | 2 +- .../src/test/resources/schema_v7_ko.json | 2 +- 9 files changed, 31 insertions(+), 25 deletions(-) diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index cc1c71bda..226e10bb6 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -83,7 +83,9 @@ You can validate inbound and outbound events using `@Validation` annotation. You can also use the `Validator#validate()` methods, if you want more control over the validation process such as handling a validation error. -We support JSON schema version 4, 6, 7 and 201909 (from [jmespath-jackson library](https://github.com/burtcorp/jmespath-java)). +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)). + +The validator is configured to enable format assertions by default even for 2019-09 and 2020-12. ### Validation annotation @@ -228,7 +230,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" 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 index 324c77a34..41696943a 100644 --- 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 @@ -85,7 +85,7 @@ void test_invalidInboundSQSEvent() throws IOException { // THEN // invocation should fail inbound validation and return an error message JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult()); - assertThat(validJsonNode.get("errorMessage").asText()).contains("$.price: is missing but it is required"); + assertThat(validJsonNode.get("errorMessage").asText()).contains(": required property 'price' not found"); } @Test @@ -99,6 +99,6 @@ void test_invalidOutboundSQSEvent() throws IOException { // 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"); + 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 index af7c7d87c..425399c95 100644 --- 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 @@ -86,7 +86,7 @@ void test_invalidInboundApiGWEvent() throws IOException { // 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("$.price: is missing but it is required"); + assertThat(validJsonNode.get("body").asText()).contains(": required property 'price' not found"); } @Test @@ -102,6 +102,6 @@ void test_invalidOutboundApiGWEvent() throws IOException { 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"); + .contains("/price: must have an exclusive maximum value of 1000"); } } diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 0de38c1c1..fa299b591 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -65,7 +65,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.0.87</version> + <version>1.4.3</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> 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 ccc5a4c2c..3f643ab00 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 @@ -47,9 +47,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, 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) { 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 221e5fb1d..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,18 +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 com.networknt.schema.uri.URITranslator; 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 @@ -255,27 +253,26 @@ 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)) { - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addUriTranslator(URITranslator.prefix("https://json-schema.org", "resource:")); - - jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream, config); + 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 schemaId = ValidationConfig.get().getSchemaVersion().getId().replace("https://json-schema.org", ""); + String schemaId = jsonSchema.getValidationContext().getMetaSchema().getIri() + .replace("https://json-schema.org", "").replace("http://json-schema.org", ""); try { validate(jsonSchema.getSchemaNode(), getJsonSchema(CLASSPATH + schemaId)); 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 d80670669..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 /draft-07/schema"); + "The schema classpath:/schema_v7_ko.json is not valid, it does not respect the specification /draft-07/schema#"); } @Test 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", From 749e9730cc3e5f7f87297c74837ae7f126b30fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:46:14 +0000 Subject: [PATCH 0621/1008] chore(v2): remove deprecated code (#1624) * remove deprecated code * fix tests * fix tests * remove v1 workflows * remove sqs example in doc --- .github/workflows/build-docs.yml | 1 - .github/workflows/pr_artifacts_size.yml | 6 +- .github/workflows/pr_build.yml | 18 ++-- .github/workflows/pr_build_v2.yml | 93 ------------------- .github/workflows/pr_iac_lint.yml | 2 - .github/workflows/run-e2e-tests-v2.yml | 58 ------------ .github/workflows/run-e2e-tests.yml | 8 +- .github/workflows/spotbugs.yml | 1 - examples/README.md | 1 - .../powertools/cloudformation/Response.java | 34 ------- .../CloudFormationResponseTest.java | 10 +- .../cloudformation/ResponseTest.java | 16 ++-- .../NoPhysicalResourceIdSetHandler.java | 6 +- .../powertools/metrics/MetricsUtils.java | 32 ------- .../powertools/metrics/MetricsLoggerTest.java | 12 --- powertools-tracing/pom.xml | 5 + .../lambda/powertools/tracing/Tracing.java | 16 ---- .../tracing/internal/LambdaTracingAspect.java | 10 +- ...erToolEnabledWithNoMetaDataDeprecated.java | 28 ------ .../internal/LambdaTracingAspectTest.java | 67 ++++++++----- 20 files changed, 78 insertions(+), 346 deletions(-) delete mode 100644 .github/workflows/pr_build_v2.yml delete mode 100644 .github/workflows/run-e2e-tests-v2.yml delete mode 100644 powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index a4ab6e7de..a75c13b52 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -3,7 +3,6 @@ name: Build Docs on: pull_request: branches: - - main - v2 paths: - 'docs/**' diff --git a/.github/workflows/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml index e7e3158e3..c4d29205b 100644 --- a/.github/workflows/pr_artifacts_size.yml +++ b/.github/workflows/pr_artifacts_size.yml @@ -3,13 +3,11 @@ name: Artifacts Size on: pull_request: branches: - - main - v2 paths: - 'powertools-batch/**' - 'powertools-cloudformation/**' - - 'powertools-core/**' # not in v2 - - 'powertools-common/**' # v2 only + - 'powertools-common/**' - 'powertools-e2e-tests/**' - 'powertools-idempotency-core/**' - 'powertools-idempotency-dynamodb/**' @@ -18,8 +16,6 @@ on: - 'powertools-metrics/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - - 'powertools-sqs/**' # not in v2 - - 'powertools-test-suite/**' # not in v2 - 'powertools-tracing/**' - 'powertools-validation/**' - 'pom.xml' diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 1ff2dc48b..2079bcb6b 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -3,11 +3,11 @@ name: Build on: pull_request: branches: - - main + - v2 paths: - 'powertools-batch/**' - 'powertools-cloudformation/**' - - 'powertools-core/**' + - 'powertools-common/**' - 'powertools-e2e-tests/**' - 'powertools-idempotency/**' - 'powertools-large-messages/**' @@ -15,8 +15,6 @@ on: - 'powertools-metrics/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - - 'powertools-sqs/**' - - 'powertools-test-suite/**' - 'powertools-tracing/**' - 'powertools-validation/**' - 'examples/**' @@ -25,11 +23,11 @@ on: - '.github/workflows/**' push: branches: - - main + - v2 paths: - 'powertools-batch/**' - 'powertools-cloudformation/**' - - 'powertools-core/**' + - 'powertools-common/**' - 'powertools-e2e-tests/**' - 'powertools-idempotency/**' - 'powertools-large-messages/**' @@ -37,8 +35,6 @@ on: - 'powertools-metrics/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - - 'powertools-sqs/**' - - 'powertools-test-suite/**' - 'powertools-tracing/**' - 'powertools-validation/**' - 'examples/**' @@ -70,12 +66,10 @@ jobs: - name: Build with Maven run: mvn -B install --file pom.xml - name: Build Gradle Example - Java - if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 - working-directory: examples/powertools-examples-core/gradle + working-directory: examples/powertools-examples-core-utilities/gradle run: ./gradlew build - name: Build Gradle Example - Kotlin - if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 - working-directory: examples/powertools-examples-core/kotlin + working-directory: examples/powertools-examples-core-utilities/kotlin run: ./gradlew build - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 diff --git a/.github/workflows/pr_build_v2.yml b/.github/workflows/pr_build_v2.yml deleted file mode 100644 index 2079bcb6b..000000000 --- a/.github/workflows/pr_build_v2.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Build - -on: - pull_request: - branches: - - v2 - paths: - - 'powertools-batch/**' - - 'powertools-cloudformation/**' - - 'powertools-common/**' - - 'powertools-e2e-tests/**' - - 'powertools-idempotency/**' - - 'powertools-large-messages/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'examples/**' - - 'pom.xml' - - 'examples/pom.xml' - - '.github/workflows/**' - push: - branches: - - v2 - paths: - - 'powertools-batch/**' - - 'powertools-cloudformation/**' - - 'powertools-common/**' - - 'powertools-e2e-tests/**' - - 'powertools-idempotency/**' - - 'powertools-large-messages/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'examples/**' - - 'pom.xml' - - 'examples/pom.xml' - - '.github/workflows/**' -jobs: - build-corretto: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - java: [11, 17, 21] - name: Java ${{ matrix.java }} - env: - JAVA: ${{ matrix.java }} - AWS_REGION: eu-west-1 - permissions: - id-token: write # needed to interact with GitHub's OIDC Token endpoint. - contents: read - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: ${{ matrix.java }} - cache: 'maven' - - name: Build with Maven - run: mvn -B install --file pom.xml - - name: Build Gradle Example - Java - working-directory: examples/powertools-examples-core-utilities/gradle - run: ./gradlew build - - name: Build Gradle Example - Kotlin - working-directory: examples/powertools-examples-core-utilities/kotlin - run: ./gradlew build - - name: Upload coverage to Codecov - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.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,./powertools-large-messages/target/site/jacoco/jacoco.xml,./powertools-batch/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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - name: Upload artifact - with: - name: pr - path: pr/ diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml index 09ba5f02b..531ccbbcb 100644 --- a/.github/workflows/pr_iac_lint.yml +++ b/.github/workflows/pr_iac_lint.yml @@ -3,11 +3,9 @@ name: Validate IaC on: push: branches: - - main - v2 pull_request: branches: - - main - v2 paths: - 'examples/**' diff --git a/.github/workflows/run-e2e-tests-v2.yml b/.github/workflows/run-e2e-tests-v2.yml deleted file mode 100644 index 255c89cfe..000000000 --- a/.github/workflows/run-e2e-tests-v2.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Run end-to-end tests - -on: - workflow_dispatch: - - push: - branches: - - v2 - paths: # add other modules when there are under e2e tests - - 'powertools-e2e-tests/**' - - 'powertools-batch/**' - - 'powertools-core/**' - - 'powertools-common/**' - - 'powertools-idempotency/**' - - 'powertools-large-message/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'pom.xml' - - '.github/workflows/**' - - pull_request: - branches: - - v2 - paths: - - 'powertools-e2e-tests/**' - -jobs: - e2e: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - java: [ 11, 17, 21 ] - name: End-to-end tests java${{ matrix.java }} - env: - AWS_DEFAULT_REGION: eu-west-1 - JAVA_VERSION: ${{ matrix.java }} - permissions: - id-token: write # needed to interact with GitHub's OIDC Token endpoint. - contents: read - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: ${{ matrix.java }} - cache: maven - - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 - 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/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 86d66156b..255c89cfe 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -5,7 +5,7 @@ on: push: branches: - - main + - v2 paths: # add other modules when there are under e2e tests - 'powertools-e2e-tests/**' - 'powertools-batch/**' @@ -23,7 +23,7 @@ on: pull_request: branches: - - main + - v2 paths: - 'powertools-e2e-tests/**' @@ -54,7 +54,5 @@ jobs: with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_DEFAULT_REGION }} - - name: Build with Maven - run: mvn -DskipTests install --file pom.xml - name: Run e2e test with Maven - run: mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml + 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/spotbugs.yml b/.github/workflows/spotbugs.yml index a75cba8f1..106905a70 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -3,7 +3,6 @@ name: SpotBugs on: pull_request: branches: - - main - v2 paths: - 'powertools-cloudformation/**' diff --git a/examples/README.md b/examples/README.md index 2e34513db..41640b5ad 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,7 +14,6 @@ Each example can be copied from its subdirectory and used independently of the r * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [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) * [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 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..215151d44 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 @@ -47,23 +47,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 +63,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. * 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..0701c98fe 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 @@ -38,7 +38,7 @@ import software.amazon.awssdk.utils.StringInputStream; import software.amazon.lambda.powertools.cloudformation.CloudFormationResponse.ResponseBody; -public class CloudFormationResponseTest { +class CloudFormationResponseTest { /** * Creates a mock CloudFormationCustomResourceEvent with a non-null response URL. @@ -215,7 +215,7 @@ void reasonIncludesLogStreamName() { } @Test - public void sendWithNoResponseData() throws Exception { + void sendWithNoResponseData() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); Context context = mock(Context.class); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); @@ -237,7 +237,7 @@ public void sendWithNoResponseData() throws Exception { } @Test - public void sendWithNonNullResponseData() throws Exception { + void sendWithNonNullResponseData() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); Context context = mock(Context.class); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); @@ -289,7 +289,7 @@ void responseBodyStreamSuccessResponse() throws Exception { Context context = mock(Context.class); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); - StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.success()); + StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.success(null)); String expectedJson = "{" + "\"Status\":\"SUCCESS\"," + @@ -310,7 +310,7 @@ void responseBodyStreamFailedResponse() throws Exception { Context context = mock(Context.class); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); - StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.failed()); + StringInputStream stream = cfnResponse.responseBodyStream(event, context, Response.failed(null)); String expectedJson = "{" + "\"Status\":\"FAILED\"," + 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..e577aecca 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 @@ -22,7 +22,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; -public class ResponseTest { +class ResponseTest { @Test void defaultValues() { @@ -92,7 +92,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 +105,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 +119,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 +135,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 +154,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 +168,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/handlers/NoPhysicalResourceIdSetHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java index 2bbda309f..e55abca03 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 @@ -23,16 +23,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-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java index ba53bad1f..6c3a89a65 100644 --- 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 @@ -14,7 +14,6 @@ package software.amazon.lambda.powertools.metrics; -import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.getXrayTraceId; import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.REQUEST_ID_PROPERTY; @@ -63,23 +62,6 @@ 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. @@ -146,20 +128,6 @@ public static void withMetricsLogger(final Consumer<MetricsLogger> logger) { } } - /** - * 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); } 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 index 26ae41a00..518e11739 100644 --- 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 @@ -188,18 +188,6 @@ 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"); - } - @Test void shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent() { try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 9d60d4f40..345f14194 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -78,6 +78,11 @@ <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> 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/internal/LambdaTracingAspect.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java index 198cb7f34..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 @@ -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/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java deleted file mode 100644 index c13f28df0..000000000 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java +++ /dev/null @@ -1,28 +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.tracing.handlers; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.lambda.powertools.tracing.Tracing; - -public class PowerTracerToolEnabledWithNoMetaDataDeprecated implements RequestHandler<Object, Object> { - - @Override - @Tracing(captureResponse = false, captureError = false) - public Object handleRequest(Object input, Context context) { - return null; - } -} 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 a676bf683..4aefdec9c 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 @@ -33,6 +33,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.Mock; import org.mockito.MockedStatic; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; @@ -47,7 +48,6 @@ 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; class LambdaTracingAspectTest { @@ -114,6 +114,28 @@ void shouldCaptureNonHandlerMethodWithCustomSegmentName() { void shouldCaptureTraces() { 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(0); + }); + } + + @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "true") + void shouldCaptureTracesWithResponseMetadata() { + requestHandler.handleRequest(new Object(), context); + assertThat(AWSXRay.getTraceEntity()) .isNotNull(); @@ -133,6 +155,7 @@ void shouldCaptureTraces() { } @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_ERROR", value = "true") void shouldCaptureTracesWithExceptionMetaData() { requestHandler = new PowerTracerToolEnabledWithException(); @@ -164,6 +187,25 @@ void shouldCaptureTracesWithExceptionMetaData() { void shouldCaptureTracesForStream() 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) + .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(); @@ -246,29 +288,6 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { }); } - @Test - void shouldCaptureTracesWithNoMetadataDeprecated() { - requestHandler = new PowerTracerToolEnabledWithNoMetaDataDeprecated(); - - 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", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); - } - @Test void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { From 1fa11c138529ee7d49f6d24415a5de6223330aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:47:05 +0000 Subject: [PATCH 0622/1008] feat(v2): parallel batch processing (#1620) * implement parallel processing * test parallel processing * document parallel processing * code review * sqs // batch processing example * rollback to AtomicBoolean * complete sqs batch example with logs and traces annotations * update doc * update doc * update doc --- docs/utilities/batch.md | 94 ++-- .../deploy/sqs/template.yml | 63 ++- examples/powertools-examples-batch/pom.xml | 11 + .../batch/sqs/AbstractSqsBatchHandler.java | 70 +++ .../org/demo/batch/sqs/SqsBatchHandler.java | 15 +- .../org/demo/batch/sqs/SqsBatchSender.java | 25 +- .../batch/sqs/SqsParallelBatchHandler.java | 48 ++ .../src/main/resources/LogLayout.json | 75 +++ .../src/main/resources/log4j2.xml | 3 +- powertools-batch/pom.xml | 16 + .../batch/handler/BatchMessageHandler.java | 17 +- .../handler/DynamoDbBatchMessageHandler.java | 77 +++- .../KinesisStreamsBatchMessageHandler.java | 93 ++-- .../batch/handler/SqsBatchMessageHandler.java | 105 +++-- .../batch/internal/MultiThreadMDC.java | 47 ++ .../batch/DdbBatchProcessorTest.java | 88 +++- .../batch/KinesisBatchProcessorTest.java | 92 +++- .../batch/SQSBatchProcessorTest.java | 89 +++- .../src/test/resources/dynamo_event_big.json | 376 +++++++++++++++ .../src/test/resources/kinesis_event_big.json | 224 +++++++++ .../src/test/resources/sqs_event_big.json | 429 ++++++++++++++++++ 21 files changed, 1877 insertions(+), 180 deletions(-) create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/AbstractSqsBatchHandler.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsParallelBatchHandler.java create mode 100644 examples/powertools-examples-batch/src/main/resources/LogLayout.json create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/MultiThreadMDC.java create mode 100644 powertools-batch/src/test/resources/dynamo_event_big.json create mode 100644 powertools-batch/src/test/resources/kinesis_event_big.json create mode 100644 powertools-batch/src/test/resources/sqs_event_big.json diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 7b571bc6e..7693ac98f 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,51 @@ 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! + + +=== "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 + } + } + ``` + ## Handling Messages @@ -490,7 +526,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 +541,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 +565,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 - 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); +```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); ``` !!! info diff --git a/examples/powertools-examples-batch/deploy/sqs/template.yml b/examples/powertools-examples-batch/deploy/sqs/template.yml index 764ba4863..2f1d6c363 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: @@ -96,11 +97,57 @@ 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 + 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: 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 @@ -121,13 +168,19 @@ 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: 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 207040476..6eb548937 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -42,6 +42,17 @@ <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> 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..25dba47bb --- /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 com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.databind.ObjectMapper; +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 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.logging.Logging; +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 + * @param context Lambda context + */ + @Logging + @Tracing + protected void processMessage(Product p, Context context) { + 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 27689485c..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,13 +4,15 @@ 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.demo.batch.model.Product; 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> { +public class SqsBatchHandler extends AbstractSqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { private static final Logger LOGGER = LoggerFactory.getLogger(SqsBatchHandler.class); private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; @@ -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/SqsBatchSender.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchSender.java index 4050ab98b..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.demo.batch.model.Product; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.demo.batch.model.Product; 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; /** @@ -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/powertools-batch/pom.xml b/powertools-batch/pom.xml index 1886f56e6..66a5e3087 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -21,6 +21,16 @@ <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> @@ -47,6 +57,12 @@ <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>2.0.7</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> 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..18d74bb25 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 @@ -33,6 +33,21 @@ 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); } 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 83a8bf7dd..4b03d0947 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 @@ -17,12 +17,14 @@ 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.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; /** * A batch message processor for DynamoDB Streams batches. @@ -46,35 +48,60 @@ 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)); + @Override + public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context context) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .parallelStream() // Parallel processing + .map(eventRecord -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + return processBatchItem(eventRecord, context); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); + } - // 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); - } + 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 (Throwable t) { + String sequenceNumber = streamRecord.getDynamodb().getSequenceNumber(); + LOGGER.error("Error while processing record with id {}: {}, adding it to batch item failures", + sequenceNumber, t.getMessage()); + LOGGER.error("Error was", t); + + // 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, t); + } catch (Throwable t2) { + LOGGER.warn("failureHandler threw handling failure", t2); } } + 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 ad1dd302d..7b4179de7 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 @@ -18,12 +18,14 @@ 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.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; import software.amazon.lambda.powertools.utilities.EventDeserializer; /** @@ -57,42 +59,67 @@ 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(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .parallelStream() // Parallel processing + .map(eventRecord -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + return processBatchItem(eventRecord, context); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + 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 (Throwable t) { + String sequenceNumber = eventRecord.getEventID(); + LOGGER.error("Error while processing record with eventID {}: {}, adding it to batch item failures", + sequenceNumber, t.getMessage()); + LOGGER.error("Error was", t); + + // 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, t); + } catch (Throwable t2) { + LOGGER.warn("failureHandler threw handling failure", t2); } } - } - 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 b634f9b62..2dfb0a28e 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 @@ -18,10 +18,15 @@ 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.atomic.AtomicBoolean; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; import software.amazon.lambda.powertools.utilities.EventDeserializer; /** @@ -61,57 +66,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); - } - - } 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); - } - } - - } + }); } - if (failWholeBatch) { + if (failWholeBatch.get()) { // Add the remaining messages to the batch item failures event.getRecords() .subList(messageCursor, event.getRecords().size()) @@ -121,4 +96,60 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { } return response; } + + @Override + public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) { + if (!event.getRecords().isEmpty() && event.getRecords().get(0).getAttributes().get(MESSAGE_GROUP_ID_KEY) != null) { + throw new UnsupportedOperationException("FIFO queues are not supported in parallel mode, use the processBatch method instead"); + } + + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + List<SQSBatchResponse.BatchItemFailure> batchItemFailures = event.getRecords() + .parallelStream() // Parallel processing + .map(sqsMessage -> { + + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + return processBatchItem(sqsMessage, context); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + 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 (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); + + // 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); + } + } + return Optional.of(SQSBatchResponse.BatchItemFailure.builder().withItemIdentifier(message.getMessageId()) + .build()); + } + } } 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..df1c2e7a0 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/MultiThreadMDC.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.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); + } + } +} 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..6bb247323 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,16 +20,27 @@ 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.atomic.AtomicBoolean; +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 } @@ -40,9 +51,36 @@ 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"); + } + } + @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 +90,28 @@ 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) { + // Arrange + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessageInParallelSucceeds); + + // Act + StreamsEventResponse dynamodbBatchResponse = handler.processBatchInParallel(event, context); + + // 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 +126,27 @@ 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) { + // Arrange + BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withDynamoDbBatchHandler() + .buildWithRawMessageHandler(this::processMessageInParallelFailsForFixedMessage); + + // Act + StreamsEventResponse dynamodbBatchResponse = handler.processBatchInParallel(event, context); + + // 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 +164,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 +172,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,7 +190,7 @@ 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"); 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..059a4d2d0 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,28 @@ 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.atomic.AtomicBoolean; +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 +53,34 @@ 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"); + } + } + // 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 +90,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 +100,28 @@ 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) { + // Arrange + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithRawMessageHandler(this::processMessageInParallelSucceeds); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatchInParallel(event, context); + + // 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 +137,28 @@ public void shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEven "49545115243490985018280067714973144582180062593244200961"); } + @ParameterizedTest + @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) + void batchProcessingInParallel_shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { + // Arrange + BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() + .withKinesisBatchHandler() + .buildWithRawMessageHandler(this::processMessageInParallelFailsForFixedMessage); + + // Act + StreamsEventResponse kinesisBatchResponse = handler.processBatchInParallel(event, context); + + // 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 +176,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 +192,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 +201,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,7 +220,7 @@ 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( 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..7dd51374e 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,43 @@ 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.atomic.AtomicBoolean; +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) { + 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,8 +64,23 @@ 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"); } @@ -50,7 +88,7 @@ public void processMessageFailsForFixedProduct(Product product, Context context) @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 +98,28 @@ 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) { + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessageInParallelSucceeds); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatchInParallel(event, context); + + // 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 +134,27 @@ 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) { + // Arrange + BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessageInParallelFailsForFixedMessage); + + // Act + SQSBatchResponse sqsBatchResponse = handler.processBatchInParallel(event, context); + + // 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 +174,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 +192,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 +215,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() 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 From 754454a92cfe4e6c69eed86ddf5fedf1d59bf4c7 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden <jeromevdl@gmail.com> Date: Thu, 4 Jul 2024 10:03:24 +0200 Subject: [PATCH 0623/1008] improve tracing doc for xray --- docs/core/tracing.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 4cb06fc29..7ad896462 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -308,8 +308,24 @@ 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). +Powertools for Lambda (Java) cannot intercept SDK clients instantiation to add X-Ray instrumentation. You should make sure to instrument the SDK clients explicitly. Refer details on +[how to instrument SDK client with Xray](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-awssdkclients) +and [outgoing http calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-httpclients). For example: + +=== "LambdaHandler.java" + + ```java hl_lines="1 2 7" + import com.amazonaws.xray.AWSXRay; + import com.amazonaws.xray.handlers.TracingHandler; + + public class LambdaHandler { + private AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() + .withRegion(Regions.fromName(System.getenv("AWS_REGION"))) + .withRequestHandlers(new TracingHandler(AWSXRay.getGlobalRecorder())) + .build(); + // ... + } + ``` ## Testing your code From 8cb9f0a50b38ebd616b8c789259e9a4520d6787b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:01:37 +0000 Subject: [PATCH 0624/1008] build(deps): bump org.assertj:assertj-core from 3.25.3 to 3.26.3 (#1695) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.25.3 to 3.26.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.25.3...assertj-build-3.26.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ba84d4fc..81aa226d7 100644 --- a/pom.xml +++ b/pom.xml @@ -298,7 +298,7 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.25.3</version> + <version>3.26.3</version> <scope>test</scope> <exclusions> <exclusion> From e5ce29901aff6d0cd041144b20eaab53643234b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:10:23 +0200 Subject: [PATCH 0625/1008] build(deps): bump com.networknt:json-schema-validator (#1705) Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.4.3 to 1.5.1. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.4.3...1.5.1) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index fa299b591..c1521f1c7 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -65,7 +65,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.4.3</version> + <version>1.5.1</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 2c087103bda1671414c2b5c37d6c801ff2337a8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:34:59 +0200 Subject: [PATCH 0626/1008] build(deps): bump jackson.version from 2.17.1 to 2.17.2 (#1688) Bumps `jackson.version` from 2.17.1 to 2.17.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 81aa226d7..eccaf3d2d 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ <log4j.version>2.20.0</log4j.version> <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> - <jackson.version>2.17.1</jackson.version> + <jackson.version>2.17.2</jackson.version> <aws.sdk.version>2.25.35</aws.sdk.version> <aws.xray.recorder.version>2.16.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> From 4a49c2a87426f13edf97bec902c4495b5d44df32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:42:57 +0200 Subject: [PATCH 0627/1008] build(deps): bump org.apache.commons:commons-lang3 from 3.13.0 to 3.15.0 (#1699) Bumps org.apache.commons:commons-lang3 from 3.13.0 to 3.15.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eccaf3d2d..fe421442a 100644 --- a/pom.xml +++ b/pom.xml @@ -286,7 +286,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.13.0</version> + <version>3.15.0</version> <scope>test</scope> </dependency> <dependency> From 97e7c49080ddafabd335dec3672bcbdb2572ecf0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:43:37 +0200 Subject: [PATCH 0628/1008] build(deps): bump aws.sdk.version from 2.25.35 to 2.26.28 (#1707) Bumps `aws.sdk.version` from 2.25.35 to 2.26.28. Updates `software.amazon.awssdk:bom` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:http-client-spi` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:url-connection-client` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:dynamodb` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:s3` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:lambda` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:kinesis` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:cloudwatch` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:xray` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:sqs` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:cloudformation` from 2.25.35 to 2.26.28 Updates `software.amazon.awssdk:sts` from 2.25.35 to 2.26.28 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 0aa8fab83..fa6a9136c 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.25.35</aws.sdk.version> + <aws.sdk.version>2.26.28</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index fe421442a..b19520867 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.25.35</aws.sdk.version> + <aws.sdk.version>2.26.28</aws.sdk.version> <aws.xray.recorder.version>2.16.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 91cfec235c7ab273d206c7cc677dbbad68cb2d69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 13:20:12 +0200 Subject: [PATCH 0629/1008] build(deps): bump ch.qos.logback:logback-classic from 1.5.5 to 1.5.7 (#1718) Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.5 to 1.5.7. - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.5...v_1.5.7) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-logging/powertools-logging-logback/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 11fc85b72..f723be91b 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -28,7 +28,7 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.5.5</version> + <version>1.5.7</version> <exclusions> <exclusion> <groupId>com.sun.mail</groupId> From 39828b3bad357e1b825f612bd387d50277df4f5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:52:30 +0200 Subject: [PATCH 0630/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1723) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.130.0 to 2.154.1. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.130.0...v2.154.1) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 100928618..5c96f3ec4 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.130.0</cdk.version> + <cdk.version>2.154.1</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 5171bb418..f66c5959a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.3.0</constructs.version> - <cdk.version>2.130.0</cdk.version> + <cdk.version>2.154.1</cdk.version> </properties> <dependencies> From c091a83d0a1b9d5d058da4040d2aa1ff889d81c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:58:30 +0200 Subject: [PATCH 0631/1008] build(deps): bump aws.xray.recorder.version from 2.16.0 to 2.18.1 (#1727) Bumps `aws.xray.recorder.version` from 2.16.0 to 2.18.1. Updates `com.amazonaws:aws-xray-recorder-sdk-core` from 2.16.0 to 2.18.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.16.0...v2.18.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core` from 2.16.0 to 2.18.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.16.0...v2.18.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2` from 2.16.0 to 2.18.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.16.0...v2.18.1) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.16.0 to 2.18.1 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.16.0...v2.18.1) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b19520867..cd58d3b7b 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> <aws.sdk.version>2.26.28</aws.sdk.version> - <aws.xray.recorder.version>2.16.0</aws.xray.recorder.version> + <aws.xray.recorder.version>2.18.1</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.3</lambda.core.version> From e74afda303fc8017c6c7342d6b33e821855df540 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:59:07 +0200 Subject: [PATCH 0632/1008] build(deps): bump com.puppycrawl.tools:checkstyle (#1728) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.12.3 to 10.18.1. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.3...checkstyle-10.18.1) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd58d3b7b..e43691536 100644 --- a/pom.xml +++ b/pom.xml @@ -665,7 +665,7 @@ <dependency> <groupId>com.puppycrawl.tools</groupId> <artifactId>checkstyle</artifactId> - <version>10.12.3</version> + <version>10.18.1</version> </dependency> </dependencies> <executions> From aa65755fd95251b9e214affa191c57f1504d3fbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:06:27 +0200 Subject: [PATCH 0633/1008] build(deps): bump aws.sdk.version from 2.26.28 to 2.27.17 (#1729) Bumps `aws.sdk.version` from 2.26.28 to 2.27.17. Updates `software.amazon.awssdk:bom` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:http-client-spi` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:url-connection-client` from 2.26.12 to 2.27.17 Updates `software.amazon.awssdk:dynamodb` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:s3` from 2.26.12 to 2.27.17 Updates `software.amazon.awssdk:lambda` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:kinesis` from 2.26.12 to 2.27.17 Updates `software.amazon.awssdk:cloudwatch` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:xray` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:sqs` from 2.26.12 to 2.27.17 Updates `software.amazon.awssdk:cloudformation` from 2.26.28 to 2.27.17 Updates `software.amazon.awssdk:sts` from 2.26.28 to 2.27.17 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index fa6a9136c..699f2b000 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.26.28</aws.sdk.version> + <aws.sdk.version>2.27.17</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index e43691536..84908a341 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.26.28</aws.sdk.version> + <aws.sdk.version>2.27.17</aws.sdk.version> <aws.xray.recorder.version>2.18.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From a925f4887ba0a12771b67497382e23677700d0eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:35:42 +0200 Subject: [PATCH 0634/1008] build(deps): bump aws.sdk.version from 2.27.17 to 2.27.21 (#1733) Bumps `aws.sdk.version` from 2.27.17 to 2.27.21. Updates `software.amazon.awssdk:bom` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:http-client-spi` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:url-connection-client` from 2.26.12 to 2.27.21 Updates `software.amazon.awssdk:dynamodb` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:s3` from 2.26.12 to 2.27.21 Updates `software.amazon.awssdk:lambda` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:kinesis` from 2.26.12 to 2.27.21 Updates `software.amazon.awssdk:cloudwatch` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:xray` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:sqs` from 2.26.12 to 2.27.21 Updates `software.amazon.awssdk:cloudformation` from 2.27.17 to 2.27.21 Updates `software.amazon.awssdk:sts` from 2.27.17 to 2.27.21 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 699f2b000..d0dd57188 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.27.17</aws.sdk.version> + <aws.sdk.version>2.27.21</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 84908a341..8266aedd4 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.27.17</aws.sdk.version> + <aws.sdk.version>2.27.21</aws.sdk.version> <aws.xray.recorder.version>2.18.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c9c4d0513134fa6edb154295382016d5db9b6a36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:43:00 +0200 Subject: [PATCH 0635/1008] build(deps): bump aws.sdk.version from 2.27.21 to 2.28.1 (#1736) Bumps `aws.sdk.version` from 2.27.21 to 2.28.1. Updates `software.amazon.awssdk:bom` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:http-client-spi` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:url-connection-client` from 2.26.12 to 2.28.1 Updates `software.amazon.awssdk:dynamodb` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:s3` from 2.26.12 to 2.28.1 Updates `software.amazon.awssdk:lambda` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:kinesis` from 2.26.12 to 2.28.1 Updates `software.amazon.awssdk:cloudwatch` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:xray` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:sqs` from 2.26.12 to 2.28.1 Updates `software.amazon.awssdk:cloudformation` from 2.27.21 to 2.28.1 Updates `software.amazon.awssdk:sts` from 2.27.21 to 2.28.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d0dd57188..782162029 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.11.3</lambda.events.version> - <aws.sdk.version>2.27.21</aws.sdk.version> + <aws.sdk.version>2.28.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 8266aedd4..c17c88736 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <log4j.version>2.23.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.27.21</aws.sdk.version> + <aws.sdk.version>2.28.1</aws.sdk.version> <aws.xray.recorder.version>2.18.1</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c6e41b4702ad604ffde803428a3c7db3698b9841 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:43:22 +0200 Subject: [PATCH 0636/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1737) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.154.1 to 2.158.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.154.1...v2.158.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 5c96f3ec4..6ab3a7601 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.154.1</cdk.version> + <cdk.version>2.158.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.10.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index f66c5959a..1a147922b 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.3.0</constructs.version> - <cdk.version>2.154.1</cdk.version> + <cdk.version>2.158.0</cdk.version> </properties> <dependencies> From a22dc0cafa507bed88001b29f8c195c2ee8c1f8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:02:56 +0200 Subject: [PATCH 0637/1008] build(deps): bump org.junit.jupiter:junit-jupiter-api (#1743) Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.10.2 to 5.11.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.11.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 422a8ebb5..2a866fa82 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -69,7 +69,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.10.2</version> + <version>5.11.1</version> <scope>test</scope> </dependency> </dependencies> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 340bcbafb..cb0fc4474 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -56,7 +56,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.10.2</version> + <version>5.11.1</version> <scope>test</scope> </dependency> <dependency> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index d1973f3df..c021572f8 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -59,7 +59,7 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.10.2</version> + <version>5.11.1</version> <scope>test</scope> </dependency> <dependency> From 95a91302aabe304f14f0d6b0a2c9056c7fdf93f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:04:13 +0200 Subject: [PATCH 0638/1008] build(deps-dev): bump org.junit.jupiter:junit-jupiter (#1744) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.10.0 to 5.11.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.0...r5.11.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 6ab3a7601..2cd4c6043 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -9,7 +9,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.158.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.10.0</junit.version> + <junit.version>5.11.1</junit.version> </properties> <build> <plugins> From 697f176d9d8e3e43e0cb1a5ac0a95f9d1dd6966a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:49:47 +0200 Subject: [PATCH 0639/1008] build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib (#1749) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.158.0 to 2.162.1. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/v2.162.1/CHANGELOG.v2.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.158.0...v2.162.1) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 2cd4c6043..fca756921 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.158.0</cdk.version> + <cdk.version>2.162.1</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.11.1</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1a147922b..1bf6310f4 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.3.0</constructs.version> - <cdk.version>2.158.0</cdk.version> + <cdk.version>2.162.1</cdk.version> </properties> <dependencies> From 72cac418c2136d7956dbc4690051c97bddc8b674 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:20:38 +0100 Subject: [PATCH 0640/1008] build(deps): bump log4j.version from 2.23.1 to 2.24.3 (#1783) Bumps `log4j.version` from 2.23.1 to 2.24.3. Updates `org.apache.logging.log4j:log4j-core` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-api` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-jcl` from 2.23.1 to 2.24.3 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index f942fe1fa..22a1b0d32 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -11,7 +11,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.23.1</log4j.version> + <log4j.version>2.24.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> diff --git a/pom.xml b/pom.xml index c17c88736..d07005c70 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ <maven.compiler.target>11</maven.compiler.target> <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.20.0</log4j.version> - <log4j.version>2.23.1</log4j.version> + <log4j.version>2.24.3</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> <aws.sdk.version>2.28.1</aws.sdk.version> From cef1ef17c28577d20d42716bbbd60e68a0905bfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:20:58 +0100 Subject: [PATCH 0641/1008] build(deps): bump aws.sdk.version from 2.26.12 to 2.30.31 (#1782) Bumps `aws.sdk.version` from 2.26.12 to 2.30.31. Updates `software.amazon.awssdk:url-connection-client` from 2.26.12 to 2.30.31 Updates `software.amazon.awssdk:sdk-core` from 2.26.12 to 2.30.31 Updates `software.amazon.awssdk:s3` from 2.26.12 to 2.30.31 Updates `software.amazon.awssdk:kinesis` from 2.26.12 to 2.30.31 Updates `software.amazon.awssdk:sqs` from 2.26.12 to 2.30.31 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.26.12 to 2.30.31 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 6eb548937..b8b50b77f 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.26.12</sdk.version> + <sdk.version>2.30.31</sdk.version> </properties> <dependencies> From 26f8b10ddcd85b9339d9543c771873d8805bddb9 Mon Sep 17 00:00:00 2001 From: RR <5737258+rr-on-gh@users.noreply.github.com> Date: Thu, 13 Mar 2025 14:14:39 +0100 Subject: [PATCH 0642/1008] feat(v2): Add GraalVM reachability metadata for core utilities (#1753) Provides GraalVM Reachability Metadata files and respective Maven configurations / profiles for building a native-image for all core-utilities. Also refactors all unit tests to use Junit Pioneer for mocking environment variables. --------- Co-authored-by: Philipp Page <pagejep@amazon.com> --- GraalVM.md | 60 + docs/FAQs.md | 111 +- docs/core/logging.md | 6 +- .../sam-graalvm/Dockerfile | 14 + .../sam-graalvm/Makefile | 6 + .../sam-graalvm/README.md | 64 + .../sam-graalvm/events/event.json | 63 + .../sam-graalvm/pom.xml | 186 +++ .../sam-graalvm/src/main/config/bootstrap | 4 + .../src/main/java/helloworld/App.java | 111 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 + .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 34 + .../resource-config.json | 19 + .../reflect-config.json | 25 + .../helloworld/native-image.properties | 1 + .../helloworld/reflect-config.json | 11 + .../helloworld/resource-config.json | 7 + .../sam-graalvm/src/main/resources/log4j2.xml | 16 + .../sam-graalvm/template.yaml | 52 + pom.xml | 1 - powertools-common/pom.xml | 102 ++ .../internal/UserAgentConfigurator.java | 12 +- .../powertools-common/jni-config.json | 22 + .../powertools-common/reflect-config.json | 189 +++ .../powertools-common/resource-config.json | 21 + .../internal/LambdaHandlerProcessorTest.java | 60 +- .../internal/UserAgentConfiguratorTest.java | 36 +- .../src/test/resources/unreadable.properties | 2 - powertools-logging/pom.xml | 102 +- .../powertools-logging-log4j/pom.xml | 102 +- .../powertools-logging-log4j/jni-config.json | 26 + .../reflect-config.json | 1225 +++++++++++++++++ .../resource-config.json | 95 ++ .../json/resolver/PowertoolsResolverTest.java | 15 +- .../powertools-logging-logback/pom.xml | 96 ++ .../jni-config.json | 26 + .../reflect-config.json | 290 ++++ .../resource-config.json | 29 + .../powertools-logging/jni-config.json | 26 + .../powertools-logging/reflect-config.json | 669 +++++++++ .../powertools-logging/resource-config.json | 23 + .../internal/LambdaLoggingAspectTest.java | 66 +- powertools-metrics/pom.xml | 104 +- .../powertools-metrics/jni-config.json | 22 + .../powertools-metrics/reflect-config.json | 285 ++++ .../powertools-metrics/resource-config.json | 19 + .../powertools/metrics/MetricsLoggerTest.java | 339 ++--- .../internal/LambdaMetricsAspectTest.java | 527 ++++--- powertools-serialization/pom.xml | 96 +- .../powertools-serialization/jni-config.json | 18 + .../reflect-config.json | 470 +++++++ .../resource-config.json | 85 ++ powertools-tracing/pom.xml | 97 ++ .../powertools-tracing/jni-config.json | 26 + .../powertools-tracing/reflect-config.json | 365 +++++ .../powertools-tracing/resource-config.json | 31 + .../internal/LambdaTracingAspectTest.java | 261 ++-- 60 files changed, 6086 insertions(+), 744 deletions(-) create mode 100644 GraalVM.md create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/Makefile create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/README.md create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/events/event.json create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/pom.xml create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/config/bootstrap create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core-utilities/sam-graalvm/template.yaml create mode 100644 powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/jni-config.json create mode 100644 powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/reflect-config.json create mode 100644 powertools-common/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common/resource-config.json delete mode 100644 powertools-common/src/test/resources/unreadable.properties create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/jni-config.json create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/reflect-config.json create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-log4j/resource-config.json create mode 100644 powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/jni-config.json create mode 100644 powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/reflect-config.json create mode 100644 powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/resource-config.json create mode 100644 powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/jni-config.json create mode 100644 powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/reflect-config.json create mode 100644 powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/resource-config.json create mode 100644 powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/jni-config.json create mode 100644 powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/reflect-config.json create mode 100644 powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/resource-config.json create mode 100644 powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json create mode 100644 powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json create mode 100644 powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json create mode 100644 powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/jni-config.json create mode 100644 powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/reflect-config.json create mode 100644 powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/resource-config.json diff --git a/GraalVM.md b/GraalVM.md new file mode 100644 index 000000000..56c72d96f --- /dev/null +++ b/GraalVM.md @@ -0,0 +1,60 @@ +# 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 + +## Reference Implementation +Working example is available in the [examples](examples/powertools-examples-core-utilities/sam-graalvm). diff --git a/docs/FAQs.md b/docs/FAQs.md index 88aa81c9f..b42302239 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -142,4 +142,113 @@ The following example shows how to use the Lambda Powertools Parameters module w } ``` The `aws-crt-client` was considered for adoption as the default HTTP client in Lambda Powertools for 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`. \ No newline at end of file +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? + +Powertools 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 5306c55df..b5dd38ccf 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -444,7 +444,7 @@ we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions) #### Custom keys -** Using StructuredArguments ** +**Using StructuredArguments** To append additional keys in your logs, you can use the `StructuredArguments` class: @@ -624,7 +624,7 @@ To append additional keys in your logs, you can use the `StructuredArguments` cl } ``` -** Using MDC ** +**Using MDC** 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: @@ -1048,4 +1048,4 @@ Use the `LambdaEcsEncoder` rather than the `LambdaJsonEncoder` when configuring <appender-ref ref="console" /> </root> </configuration> - ``` \ No newline at end of file + ``` 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..a690606ad --- /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:latest + +#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-core-utilities/sam-graalvm/events/event.json b/examples/powertools-examples-core-utilities/sam-graalvm/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file 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..4ba4db943 --- /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</name> + <groupId>software.amazon.lambda.examples</groupId> + <version>2.0.0-SNAPSHOT</version> + <artifactId>powertools-examples-core-utilitiessam-graalvm</artifactId> + <packaging>jar</packaging> + + <properties> + <log4j.version>2.24.0</log4j.version> + <maven.compiler.source>21</maven.compiler.source> + <maven.compiler.target>21</maven.compiler.target> + <aspectj.version>1.9.22.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.2.3</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.11.3</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.1.1</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.0</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.1.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.2</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.10.1</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..e7c410042 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/java/helloworld/App.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 helloworld; + +import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; +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; +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.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.StorageResolution; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; +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); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(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); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + metricsLogger().putMetric("CustomMetric3", 1, Unit.COUNT, StorageResolution.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..d30696750 --- /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.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"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..106edef38 --- /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,34 @@ +[ + { + "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.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"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/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..7cc78a494 --- /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": "\\Qaarch64/aws-lambda-runtime-interface-client.glibc.so\\E" + }, + { + "pattern": "\\Qaarch64/aws-lambda-runtime-interface-client.musl.so\\E" + }, + { + "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.glibc.so\\E" + }, + { + "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.musl.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/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml new file mode 100644 index 000000000..60975d487 --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="JsonAppender"> + <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-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/pom.xml b/pom.xml index d07005c70..01ca81458 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,6 @@ <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.20.0</log4j.version> <log4j.version>2.24.3</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 15409e1f6..2a04688fb 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -56,6 +56,11 @@ <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> @@ -72,6 +77,99 @@ <scope>test</scope> </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> <build> <resources> @@ -79,6 +177,10 @@ <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> diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java index 8ae10ad62..d2e592902 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java @@ -16,9 +16,8 @@ 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.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,11 +65,12 @@ static String getProjectVersion() { */ static String getVersionFromProperties(String propertyFileName, String versionKey) { - URL propertiesFileURI = Thread.currentThread().getContextClassLoader().getResource(propertyFileName); - if (propertiesFileURI != null) { - try (FileInputStream fis = new FileInputStream(propertiesFileURI.getPath())) { + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(propertyFileName); + + if (is != null) { + try { Properties properties = new Properties(); - properties.load(fis); + properties.load(is); String version = properties.getProperty(versionKey); if (version != null && !version.isEmpty()) { return version; 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/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index 4ad170600..15d7bccdb 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -17,20 +17,21 @@ 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.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 org.aspectj.lang.Signature; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; +import org.junitpioneer.jupiter.ClearEnvironmentVariable; +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; class LambdaHandlerProcessorTest { @@ -139,27 +140,23 @@ 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 xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); - assertThat(xRayTraceId.isPresent()).isTrue(); - assertThat(traceID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")).isEqualTo(xRayTraceId.get()); - } + assertThat(xRayTraceId.isPresent()).isTrue(); + 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 @@ -209,37 +206,28 @@ 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) { @@ -248,4 +236,4 @@ private ProceedingJoinPoint mockRequestHandlerPjp(Class handlerClass, Object[] h when(pjpMock.getSignature()).thenReturn(signature); return pjpMock; } -} \ 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 index 9e4d26504..0c7935a55 100644 --- 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 @@ -15,19 +15,19 @@ package software.amazon.lambda.powertools.common.internal; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mockStatic; 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 static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; - import java.io.File; -import java.util.Objects; +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.mockito.MockedStatic; +import org.junitpioneer.jupiter.SetEnvironmentVariable; class UserAgentConfiguratorTest { @@ -64,14 +64,18 @@ void testGetVersionFromProperties_FileNotExist() { } @Test - void testGetVersionFromProperties_InvalidFile() { - File f = new File(Objects.requireNonNull(Thread.currentThread().getContextClassLoader() - .getResource("unreadable.properties")).getPath()); + void testGetVersionFromProperties_InvalidFile() throws IOException { + Path tempFile = Files.createTempFile("unreadable", ".properties"); + File f = tempFile.toFile(); f.setReadable(false); - String version = getVersionFromProperties("unreadable.properties", VERSION_KEY); + String version = getVersionFromProperties(f.getName(), VERSION_KEY); assertThat(version).isEqualTo("NA"); + + // Cleanup + f.setReadable(true); + Files.deleteIfExists(tempFile); } @Test @@ -110,15 +114,13 @@ void testGetUserAgent_NullFeature() { } @Test + @SetEnvironmentVariable(key = AWS_EXECUTION_ENV, value = "AWS_Lambda_java8") 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"); - } + String userAgent = UserAgentConfigurator.getUserAgent("test-feature"); + + assertThat(userAgent) + .isNotNull() + .isEqualTo("PT/test-feature/" + VERSION + " PTEnv/AWS_Lambda_java8"); } } diff --git a/powertools-common/src/test/resources/unreadable.properties b/powertools-common/src/test/resources/unreadable.properties deleted file mode 100644 index 42ff4693f..000000000 --- a/powertools-common/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-logging/pom.xml b/powertools-logging/pom.xml index a1148a9cd..86aeadd28 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -67,6 +67,11 @@ <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> @@ -98,8 +103,103 @@ <scope>test</scope> </dependency> </dependencies> - + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</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> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index d76137678..5f176bb46 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -51,6 +51,11 @@ <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> @@ -82,8 +87,103 @@ <scope>test</scope> </dependency> </dependencies> - + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>--add-opens java.base/java.util=ALL-UNNAMED</buildArg> + <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> + <buildArg>--no-fallback</buildArg> + <buildArg>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</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> 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..2c4de0562 --- /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,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-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..9b2afe183 --- /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,1225 @@ +[ +{ + "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", + "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.services.lambda.runtime.RequestHandler", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "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":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"jakarta.servlet.Servlet" +}, +{ + "name":"java.io.Serializable", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":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.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Comparable", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"java.lang.Enum", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":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", + "allDeclaredFields":true, + "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.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"getSecurityManager","parameterTypes":[] }] +}, +{ + "name":"java.lang.Thread", + "fields":[{"name":"threadLocalRandomProbe"}] +}, +{ + "name":"java.lang.annotation.Retention", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.annotation.Target", + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.constant.Constable", + "allDeclaredClasses":true, + "queryAllPublicMethods":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.sql.Date" +}, +{ + "name":"java.sql.Time" +}, +{ + "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":"java.util.concurrent.atomic.Striped64", + "fields":[{"name":"base"}, {"name":"cellsBusy"}] +}, +{ + "name":"java.util.function.Consumer", + "queryAllPublicMethods":true +}, +{ + "name":"javax.servlet.Servlet" +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "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.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" +}, +{ + "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", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] +}, +{ + "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", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] +}, +{ + "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", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] +}, +{ + "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", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] +}, +{ + "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.PowerToolsResolverFactoryTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogInEcsFormat","parameterTypes":[] }, {"name":"shouldLogInJsonFormat","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.PowertoolsResolverArgumentsTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingKeyValue","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingRawJson","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.layout.template.json.resolver.PowertoolsResolverFactory", + "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.aspectj.runtime.internal.AroundClosure", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.jctools.queues.MpmcArrayQueue" +}, +{ + "name":"org.osgi.framework.FrameworkUtil" +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"IS_COLD_START"}] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.Log4jLoggingManagerTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getLogLevel_shouldReturnConfiguredLogLevel","parameterTypes":[] }, {"name":"resetLogLevel","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments$ArgumentFormat", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +} +] 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..aca0e0356 --- /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,95 @@ +{ + "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/java.time.zone.ZoneRulesProvider\\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":"\\Qlog4j2-test.jsn\\E" + }, { + "pattern":"\\Qlog4j2-test.json\\E" + }, { + "pattern":"\\Qlog4j2-test.properties\\E" + }, { + "pattern":"\\Qlog4j2-test.xml\\E" + }, { + "pattern":"\\Qlog4j2-test.yaml\\E" + }, { + "pattern":"\\Qlog4j2-test.yml\\E" + }, { + "pattern":"\\Qlog4j2-test18b4aac2.jsn\\E" + }, { + "pattern":"\\Qlog4j2-test18b4aac2.json\\E" + }, { + "pattern":"\\Qlog4j2-test18b4aac2.properties\\E" + }, { + "pattern":"\\Qlog4j2-test18b4aac2.xml\\E" + }, { + "pattern":"\\Qlog4j2-test18b4aac2.yaml\\E" + }, { + "pattern":"\\Qlog4j2-test18b4aac2.yml\\E" + }, { + "pattern":"\\Qlog4j2.StatusLogger.properties\\E" + }, { + "pattern":"\\Qlog4j2.component.properties\\E" + }, { + "pattern":"\\Qlog4j2.jsn\\E" + }, { + "pattern":"\\Qlog4j2.json\\E" + }, { + "pattern":"\\Qlog4j2.properties\\E" + }, { + "pattern":"\\Qlog4j2.system.properties\\E" + }, { + "pattern":"\\Qlog4j2.xml\\E" + }, { + "pattern":"\\Qlog4j2.yaml\\E" + }, { + "pattern":"\\Qlog4j2.yml\\E" + }, { + "pattern":"\\Qlog4j218b4aac2.jsn\\E" + }, { + "pattern":"\\Qlog4j218b4aac2.json\\E" + }, { + "pattern":"\\Qlog4j218b4aac2.properties\\E" + }, { + "pattern":"\\Qlog4j218b4aac2.xml\\E" + }, { + "pattern":"\\Qlog4j218b4aac2.yaml\\E" + }, { + "pattern":"\\Qlog4j218b4aac2.yml\\E" + }]}, + "bundles":[] +} 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 index 073cd7026..d1b9fec83 100644 --- 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 @@ -16,8 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mockStatic; -import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; 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; @@ -32,8 +30,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.common.internal.SystemWrapper; +import org.junitpioneer.jupiter.SetEnvironmentVariable; import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; class PowertoolsResolverTest { @@ -79,14 +76,10 @@ void unknownField_shouldThrowException() { } @Test + @SetEnvironmentVariable(key = "AWS_REGION", value = "eu-central-2") void shouldResolveRegion() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> getenv("AWS_REGION")) - .thenReturn("eu-central-2"); - - String result = resolveField("region", "dummy, will use the env var"); - assertThat(result).isEqualTo("\"eu-central-2\""); - } + String result = resolveField("region", "dummy, will use the env var"); + assertThat(result).isEqualTo("\"eu-central-2\""); } private static String resolveField(String field, String value) { diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index f723be91b..8fde683b4 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -80,7 +80,103 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer version --> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <agent> + <enabled>true</enabled> + <defaultMode>Standard</defaultMode> + </agent> + <imageName>powertools-logging-logback</imageName> + <buildArgs> + <buildArg>--no-fallback</buildArg> + <buildArg>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</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> 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..2c4de0562 --- /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,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-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..0d3bdbe7e --- /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,290 @@ +[ +{ + "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":"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.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":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.FilePermission" +}, +{ + "name":"java.io.IOException" +}, +{ + "name":"java.io.InputStream" +}, +{ + "name":"java.io.OutputStream" +}, +{ + "name":"java.io.Serializable", + "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.Cloneable", + "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, + "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.RuntimePermission" +}, +{ + "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.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.net.NetPermission" +}, +{ + "name":"java.net.SocketPermission" +}, +{ + "name":"java.net.URLPermission", + "methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"java.security.AccessController", + "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] +}, +{ + "name":"java.security.AllPermission" +}, +{ + "name":"java.security.SecurityPermission" +}, +{ + "name":"java.util.PropertyPermission" +}, +{ + "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.smartcardio.CardPermission" +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"IS_COLD_START"}] +}, +{ + "name":"software.amazon.lambda.powertools.logging.LogbackLoggingManagerTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getLogLevel_shouldReturnConfiguredLogLevel","parameterTypes":[] }, {"name":"resetLogLevel","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.LambdaEcsEncoderTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogException","parameterTypes":[] }, {"name":"shouldLogInEcsFormat","parameterTypes":[] }, {"name":"shouldNotLogCloudInfo","parameterTypes":[] }, {"name":"shouldNotLogFunctionInfo","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.LambdaJsonEncoderTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingKeyValue","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingRawJson","parameterTypes":[] }, {"name":"shouldLogEventAsStringForStreamHandler","parameterTypes":[] }, {"name":"shouldLogEventForHandlerWhenEnvVariableSetToTrue","parameterTypes":[] }, {"name":"shouldLogEventForHandlerWithLogEventAnnotation","parameterTypes":[] }, {"name":"shouldLogException","parameterTypes":[] }, {"name":"shouldLogInJsonFormat","parameterTypes":[] }, {"name":"shouldLogResponseForHandlerWhenEnvVariableSetToTrue","parameterTypes":[] }, {"name":"shouldLogResponseForHandlerWithLogResponseAnnotation","parameterTypes":[] }, {"name":"shouldLogResponseForStreamHandler","parameterTypes":[] }, {"name":"shouldLogStructuredArgumentsAsNewEntries","parameterTypes":[] }, {"name":"shouldLogThreadInfo","parameterTypes":[] }, {"name":"shouldLogTimestampDifferently","parameterTypes":[] }, {"name":"shouldNotLogEventForHandlerWhenEnvVariableSetToFalse","parameterTypes":[] }, {"name":"shouldNotLogPowertoolsInfo","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments", + "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEvent", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEventDisabled", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEventForStream", + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogResponse", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogResponseForStream", + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "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":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +} +] 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..dea71883a --- /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,29 @@ +{ + "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/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\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":"\\Qlogback-test.scmo\\E" + }, { + "pattern":"\\Qlogback-test.xml\\E" + }, { + "pattern":"\\Qlogback.scmo\\E" + }]}, + "bundles":[] +} 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..2c4de0562 --- /dev/null +++ b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/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-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..7347b8400 --- /dev/null +++ b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/reflect-config.json @@ -0,0 +1,669 @@ +[ +{ + "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", + "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.services.lambda.runtime.RequestHandler", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"com.amazonaws.services.lambda.runtime.RequestStreamHandler", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "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, + "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":"com.sun.tools.attach.VirtualMachine" +}, +{ + "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, + "queryAllDeclaredConstructors":true +}, +{ + "name":"java.lang.Boolean" +}, +{ + "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.Cloneable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Comparable", + "allDeclaredClasses":true, + "queryAllPublicMethods":true +}, +{ + "name":"java.lang.Enum", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":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, + "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.constant.Constable", + "allDeclaredClasses":true, + "queryAllPublicMethods":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.Map" +}, +{ + "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.function.Consumer", + "queryAllPublicMethods":true +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, +{ + "name":"kotlin.jvm.JvmInline" +}, +{ + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true +}, +{ + "name":"org.aspectj.runtime.internal.AroundClosure", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "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":"org.slf4j.test.OutputChoice", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.test.OutputChoice$OutputChoiceType", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.test.TestLogger", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.test.TestLoggerConfiguration", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.test.TestLoggerFactory", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.slf4j.test.TestServiceProvider", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"IS_COLD_START"}, {"name":"SERVICE_NAME"}] +}, +{ + "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":"jsonArgument","parameterTypes":[] }, {"name":"keyValueArgument","parameterTypes":[] }, {"name":"mapArgument","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAppSyncCorrelationId", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAppSyncCorrelationId$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogClearState", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.util.Map","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogClearState$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogDisabled", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogDisabledForStream", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"anotherMethod","parameterTypes":[] }, {"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled$AjcClosure3", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledForStream", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledForStream$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogError", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogError$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEvent", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEvent$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventEnvVar", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventEnvVar$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventForStream", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventForStream$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponse", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponse$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponseForStream", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponseForStream$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingDisabled", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingDisabled$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingEnabled", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingEnabled$AjcClosure1", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":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, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getProducts","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.logging.model.Product", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getId","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPrice","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +} +] 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..ca77675e0 --- /dev/null +++ b/powertools-logging/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging/resource-config.json @@ -0,0 +1,23 @@ +{ + "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":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" + }, { + "pattern":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/Europe/Berlin\\E" + }, { + "pattern":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/ZoneInfoMap\\E" + }]}, + "bundles":[] +} 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 28e20aadd..557c6c893 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 @@ -18,11 +18,8 @@ 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 static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getProperty; -import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv; 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; @@ -32,15 +29,6 @@ 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.SQSEvent; -import com.amazonaws.services.lambda.runtime.tests.annotations.Event; -import com.fasterxml.jackson.databind.ObjectMapper; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -58,19 +46,32 @@ import java.util.Collections; import java.util.List; 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.params.ParameterizedTest; +import org.junitpioneer.jupiter.ClearEnvironmentVariable; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.SetSystemProperty; import org.mockito.Mock; -import org.mockito.MockedStatic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.internal.SystemWrapper; import software.amazon.lambda.powertools.logging.argument.KeyValueArgument; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId; @@ -426,40 +427,31 @@ void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { } @Test + @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"; - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn(null); - mocked.when(() -> getProperty("com.amazonaws.xray.traceHeader")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); - - requestHandler.handleRequest(new Object(), context); + requestHandler.handleRequest(new Object(), context); - assertThat(MDC.getCopyOfContextMap()) - .hasSize(EXPECTED_CONTEXT_SIZE + 1) - .containsEntry("xray_trace_id", xRayTraceId); - } + assertThat(MDC.getCopyOfContextMap()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry("xray_trace_id", xRayTraceId); } @Test + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") void shouldLogxRayTraceIdEnvVarSet() { // GIVEN String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); - - // WHEN - requestHandler.handleRequest(new Object(), context); + // WHEN + requestHandler.handleRequest(new Object(), context); - // THEN - assertThat(MDC.getCopyOfContextMap()) - .hasSize(EXPECTED_CONTEXT_SIZE + 1) - .containsEntry(FUNCTION_TRACE_ID.getName(), xRayTraceId); - } + // THEN + assertThat(MDC.getCopyOfContextMap()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry(FUNCTION_TRACE_ID.getName(), xRayTraceId); } @Test @@ -791,4 +783,4 @@ private void resetLogLevel(Level level) setLogLevels.invoke(null, level); writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); } -} \ No newline at end of file +} diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 0daa49664..1211a02c1 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -77,6 +77,11 @@ <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> @@ -94,7 +99,104 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</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> @@ -102,4 +204,4 @@ </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> 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..8ea90d67f --- /dev/null +++ b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/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-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..bf67fc97b --- /dev/null +++ b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/reflect-config.json @@ -0,0 +1,285 @@ +[ +{ + "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.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"<init>","parameterTypes":[] }] +}, +{ + "name":"com.sun.tools.attach.VirtualMachine" +}, +{ + "name":"java.io.InputStream" +}, +{ + "name":"java.io.OutputStream" +}, +{ + "name":"java.io.Serializable", + "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.Comparable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.Double", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":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.Number", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "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.constant.Constable", + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.constant.ConstantDesc", + "queryAllDeclaredMethods":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":"software.amazon.cloudwatchlogs.emf.logger.MetricsLogger", + "fields":[{"name":"context"}] +}, +{ + "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":"IS_COLD_START"}] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.MetricsLoggerTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"metricsLoggerCaptureUtilityWithDefaultNameSpace","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent","parameterTypes":[] }, {"name":"singleMetricsCaptureUtility","parameterTypes":[] }, {"name":"singleMetricsCaptureUtilityWithDefaultDimension","parameterTypes":[] }, {"name":"singleMetricsCaptureUtilityWithDefaultNameSpace","parameterTypes":[] }, {"name":"singleMetricsCaptureUtilityWithNullNamespace","parameterTypes":[] }, {"name":"tearDown","parameterTypes":[] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsColdStartEnabledHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledDefaultDimensionHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledDefaultNoDimensionHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledStreamHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsExceptionWhenNoMetricsHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsNoDimensionsHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsNoExceptionWhenNoMetricsHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsTooManyDimensionsHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsWithExceptionInHandler", + "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] +}, +{ + "name":"software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"allowWhenNoDimensionsSet","parameterTypes":[] }, {"name":"exceptionWhenNoMetricsEmitted","parameterTypes":[] }, {"name":"exceptionWhenTooManyDimensionsSet","parameterTypes":[] }, {"name":"metricsPublishedEvenHandlerThrowsException","parameterTypes":[] }, {"name":"metricsWithColdStart","parameterTypes":[] }, {"name":"metricsWithDefaultDimensionSpecified","parameterTypes":[] }, {"name":"metricsWithDefaultNoDimensionSpecified","parameterTypes":[] }, {"name":"metricsWithStreamHandler","parameterTypes":[] }, {"name":"metricsWithoutColdStart","parameterTypes":[] }, {"name":"noColdStartMetricsWhenColdStartDone","parameterTypes":[] }, {"name":"noExceptionWhenNoMetricsEmitted","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"tearDown","parameterTypes":[] }] +}, +{ + "name":"sun.reflect.ReflectionFactory", + "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] +} +] 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..dd8fabec3 --- /dev/null +++ b/powertools-metrics/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics/resource-config.json @@ -0,0 +1,19 @@ +{ + "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" + }]}, + "bundles":[] +} 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 index 518e11739..5f99c950a 100644 --- 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 @@ -16,43 +16,34 @@ 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.common.internal.SystemWrapper.getProperty; -import static software.amazon.lambda.powertools.common.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.ArrayList; import java.util.LinkedHashMap; 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 org.junitpioneer.jupiter.SetEnvironmentVariable; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.StorageResolution; import software.amazon.cloudwatchlogs.emf.model.Unit; +@SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") 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)); @@ -64,206 +55,170 @@ void tearDown() { } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") void singleMetricsCaptureUtilityWithDefaultDimension() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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"); - }); - } + 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 + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") void singleMetricsCaptureUtility() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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"); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=test"); - }); - } + 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"); + + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=test"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") void singleMetricsCaptureUtilityWithNullNamespace() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.internal.SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - // POWERTOOLS_METRICS_NAMESPACE is not defined - - MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, - metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=aws-embedded-metrics"); - }); - } + // POWERTOOLS_METRICS_NAMESPACE is not defined + + MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, + metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); + + assertThat(out.toString()) + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=aws-embedded-metrics"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "GlobalName") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") void singleMetricsCaptureUtilityWithDefaultNameSpace() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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"); - }); - } + 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 + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "GlobalName") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") void metricsLoggerCaptureUtilityWithDefaultNameSpace() { testLogger(MetricsUtils::withMetricsLogger); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "GlobalName") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") void shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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(null); - internalWrapper.when(() -> getProperty("com.amazonaws.xray.traceHeader")) - .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"); - }); - } + 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"); + }); } private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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); - metricsLogger.putMetric("Metric2", 1, Unit.COUNT, StorageResolution.HIGH); - }); - - 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"); - - ArrayList cloudWatchMetrics = (ArrayList) aws.get("CloudWatchMetrics"); - LinkedHashMap<String, Object> values = - (java.util.LinkedHashMap<String, Object>) cloudWatchMetrics.get(0); - ArrayList metricArray = (ArrayList) values.get("Metrics"); - LinkedHashMap<String, Object> metricValues = (LinkedHashMap<String, Object>) metricArray.get(1); - assertThat(metricValues).containsEntry("StorageResolution", 1); - }); - } + methodToTest.accept(metricsLogger -> + { + metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); + metricsLogger.putMetric("Metric1", 1, Unit.COUNT); + metricsLogger.putMetric("Metric2", 1, Unit.COUNT, StorageResolution.HIGH); + }); + + 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"); + + ArrayList cloudWatchMetrics = (ArrayList) aws.get("CloudWatchMetrics"); + LinkedHashMap<String, Object> values = + (java.util.LinkedHashMap<String, Object>) cloudWatchMetrics.get(0); + ArrayList metricArray = (ArrayList) values.get("Metrics"); + LinkedHashMap<String, Object> metricValues = (LinkedHashMap<String, Object>) metricArray.get(1); + assertThat(metricValues).containsEntry("StorageResolution", 1); + }); } private Map<String, Object> readAsJson(String s) { @@ -274,4 +229,4 @@ private Map<String, Object> readAsJson(String s) { } return emptyMap(); } -} \ No newline at end of file +} 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 d27af1fdf..5df6003c8 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 @@ -18,28 +18,27 @@ 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.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 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.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.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.Mock; -import org.mockito.MockedStatic; -import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; + +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 software.amazon.cloudwatchlogs.emf.exception.DimensionSetExceededException; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.MetricsUtils; @@ -55,6 +54,7 @@ import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsTooManyDimensionsHandler; import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsWithExceptionInHandler; +@SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") public class LambdaMetricsAspectTest { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; @@ -63,14 +63,6 @@ public class LambdaMetricsAspectTest { 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"); - } - } - @BeforeEach void setUp() throws IllegalAccessException { openMocks(this); @@ -85,341 +77,292 @@ void tearDown() { } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") public void metricsWithoutColdStart() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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"); - }); - } + + 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"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") public void metricsWithDefaultDimensionSpecified() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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 = new PowertoolsMetricsEnabledDefaultDimensionHandler(); + requestHandler.handleRequest("input", context); - requestHandler.handleRequest("input", context); + assertThat(out.toString().split("\n")) + .hasSize(2) + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s[0]); - 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"); - 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"); - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); + logAsJson = readAsJson(s[1]); - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("CustomDimension", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("CustomDimension", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") + @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") public void metricsWithDefaultNoDimensionSpecified() { - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class); - MockedStatic<software.amazon.lambda.powertools.common.internal.SystemWrapper> internalWrapper = mockStatic( - software.amazon.lambda.powertools.common.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 = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); + requestHandler.handleRequest("input", context); - requestHandler.handleRequest("input", context); + assertThat(out.toString().split("\n")) + .hasSize(2) + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s[0]); - assertThat(out.toString().split("\n")) - .hasSize(2) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s[0]); + assertThat(logAsJson) + .containsEntry("Metric2", 1.0) + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") + .containsEntry("function_request_id", "123ABC"); - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); + Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); + logAsJson = readAsJson(s[1]); - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") public void metricsWithColdStart() { - - 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); - - assertThat(out.toString().split("\n")) - .hasSize(2) - .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"); - }); - } + MetricsUtils.defaultDimensions(null); + requestHandler = new PowertoolsMetricsColdStartEnabledHandler(); + + requestHandler.handleRequest("input", context); + + assertThat(out.toString().split("\n")) + .hasSize(2) + .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"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") 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"); - }); - } + 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"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") public void metricsWithStreamHandler() throws IOException { - - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); - RequestStreamHandler streamHandler = new PowertoolsMetricsEnabledStreamHandler(); - - streamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); - } + MetricsUtils.defaultDimensions(null); + RequestStreamHandler streamHandler = new PowertoolsMetricsEnabledStreamHandler(); + + streamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); + + assertThat(out.toString()) + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") 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(); + 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"); - } + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> requestHandler.handleRequest("input", context)) + .withMessage("No metrics captured, at least one metrics must be emitted"); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") public void noExceptionWhenNoMetricsEmitted() { + MetricsUtils.defaultDimensions(null); + requestHandler = new PowertoolsMetricsNoExceptionWhenNoMetricsHandler(); - 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); + requestHandler.handleRequest("input", context); - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); + assertThat(out.toString()) + .satisfies(s -> + { + Map<String, Object> logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("Service", "booking") - .doesNotContainKey("_aws"); - }); - } + assertThat(logAsJson) + .containsEntry("Service", "booking") + .doesNotContainKey("_aws"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") 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"); - }); - } + 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"); + }); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") public void exceptionWhenTooManyDimensionsSet() { + MetricsUtils.defaultDimensions(null); - try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - - MetricsUtils.defaultDimensions(null); + requestHandler = new PowertoolsMetricsTooManyDimensionsHandler(); - requestHandler = new PowertoolsMetricsTooManyDimensionsHandler(); - - assertThatExceptionOfType(DimensionSetExceededException.class) - .isThrownBy(() -> requestHandler.handleRequest("input", context)) - .withMessage( - "Maximum number of dimensions allowed are 30. Account for default dimensions if not using setDimensions."); - } + assertThatExceptionOfType(DimensionSetExceededException.class) + .isThrownBy(() -> requestHandler.handleRequest("input", context)) + .withMessage( + "Maximum number of dimensions allowed are 30. Account for default dimensions if not using setDimensions."); } @Test + @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") 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"); - }); - } + 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"); + }); } private void setupContext() { diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 454b30d3e..ee2d7db3d 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -67,8 +67,102 @@ <scope>test</scope> </dependency> </dependencies> - + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer version --> + <extensions>true</extensions> + <executions> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <agent> + <enabled>true</enabled> + <defaultMode>Standard</defaultMode> + </agent> + <imageName>powertools-serialization</imageName> + <buildArgs> + <buildArg>--no-fallback</buildArg> + <buildArg>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</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> diff --git a/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json new file mode 100644 index 000000000..079c02a4d --- /dev/null +++ b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json @@ -0,0 +1,18 @@ +[ +{ + "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.management.VMManagementImpl", + "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] +} +] 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..1a4f89735 --- /dev/null +++ b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json @@ -0,0 +1,470 @@ +[ +{ + "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":"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"] }] +}, +{ + "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/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..d5bd7e14c --- /dev/null +++ b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json @@ -0,0 +1,85 @@ +{ + "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.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":"\\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":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/Europe/Berlin\\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/Berlin\\E" + }, { + "pattern":"\\Qorg/joda/time/tz/data/ZoneInfoMap\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }]}, + "bundles":[] +} diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 345f14194..59bb596db 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -100,6 +100,103 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>-H:IncludeResources=version.properties</buildArg> + <buildArg>-H:IncludeResources=unreadable.properties</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <plugins> <plugin> 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..94a514dee --- /dev/null +++ b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/reflect-config.json @@ -0,0 +1,365 @@ +[ +{ + "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":"IS_COLD_START"}] +}, +{ + "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":[] }] +} +] 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..2ac72d18f --- /dev/null +++ b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/resource-config.json @@ -0,0 +1,31 @@ +{ + "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" + }]}, + "bundles":[] +} 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 4aefdec9c..1d108ed5f 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,24 @@ 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.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.Mock; -import org.mockito.MockedStatic; + +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.tracing.handlers.PowerToolDisabled; import software.amazon.lambda.powertools.tracing.handlers.PowerToolDisabledForStream; @@ -50,6 +50,9 @@ import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaData; 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; @@ -58,16 +61,6 @@ class LambdaTracingAspectTest { @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); @@ -289,56 +282,50 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { } @Test + @SetEnvironmentVariable(key = "POWERTOOLS_TRACER_CAPTURE_RESPONSE", value = "false") 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(); - }); - } + 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 @@ -369,95 +356,85 @@ 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)); + + 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)); + }); } private void setupContext() { From 8479a7f584759dd90e1106024631b6767b79bf0e Mon Sep 17 00:00:00 2001 From: Philipp Page <pagejep@amazon.com> Date: Mon, 24 Mar 2025 13:04:56 +0100 Subject: [PATCH 0643/1008] feat(cfn-custom-resource): Add optional 'reason' field for detailed failure reporting (#1810) --- .../CloudFormationResponse.java | 5 ++- .../powertools/cloudformation/Response.java | 39 +++++++++++++++++++ .../CloudFormationResponseTest.java | 23 +++++++++++ .../cloudformation/ResponseTest.java | 23 +++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) 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..404137802 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 @@ -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 215151d44..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; } /** @@ -115,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 * @@ -127,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(",", "[", "]")); @@ -148,6 +169,7 @@ public static class Builder { private Status status; private String physicalResourceId; private boolean noEcho; + private String reason; private Builder() { } @@ -229,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. * @@ -243,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/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java index 0701c98fe..9da18790c 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 @@ -324,4 +324,27 @@ void responseBodyStreamFailedResponse() throws Exception { "}"; assertThat(stream.getString()).isEqualTo(expectedJson); } + + @Test + void responseBodyStreamFailedResponseWithReason() throws Exception { + CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); + Context context = mock(Context.class); + 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\":null," + + "\"StackId\":null," + + "\"RequestId\":null," + + "\"LogicalResourceId\":null," + + "\"NoEcho\":false," + + "\"Data\":null" + + "}"; + assertThat(stream.getString()).isEqualTo(expectedJson); + } } 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 e577aecca..3e2930541 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 @@ -33,11 +33,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 +63,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() From 0358d649badbebd40b56d61f9c1f85ed60e4ba04 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 28 Mar 2025 15:49:40 +0100 Subject: [PATCH 0644/1008] feat(idempotency): Add response hook feature (#1814) * Add responseHook definition to IdempotencyConfig and call from IdempotencyHandler. Update idempotency example with an example use-case modifying API GW headers. * Make debug log message more concise in IdempotencyHandler.java. * Add unit test for idempotency response hook. * Document idempotency response hook feature. * Update examples/powertools-examples-idempotency/src/main/java/helloworld/App.java Co-authored-by: Leandro Damascena <lcdama@amazon.pt> --------- Co-authored-by: Leandro Damascena <lcdama@amazon.pt> --- docs/utilities/idempotency.md | 60 +++++- .../src/main/java/helloworld/App.java | 42 +++-- .../idempotency/IdempotencyConfig.java | 85 +++++++-- .../internal/IdempotencyHandler.java | 47 +++-- .../internal/IdempotencyAspectTest.java | 174 +++++++++++++----- 5 files changed, 312 insertions(+), 96 deletions(-) diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 240752a55..a6da0e37e 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -55,7 +55,7 @@ times with the same parameters**. This makes idempotent operations safe to retry <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-idempotency-dynamodb</artifactId> + <artifactId>powertools-idempotency-core</artifactId> </aspectLibrary> </aspectLibraries> </configuration> @@ -584,6 +584,7 @@ IdempotencyConfig.builder() .withUseLocalCache(true) .withLocalCacheMaxItems(432) .withHashFunction("SHA-256") + .withResponseHook((responseData, dataRecord) -> responseData) .build() ``` @@ -591,13 +592,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. @@ -855,6 +857,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("IDEMPOTENCY_TABLE")) + .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 diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index 0c4693230..029877c73 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -45,20 +45,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,8 +83,10 @@ 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! @@ -101,14 +119,14 @@ 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 */ @@ -118,4 +136,4 @@ private String getPageContents(String address) throws IOException { return br.lines().collect(Collectors.joining(System.lineSeparator())); } } -} \ No newline at end of file +} diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 2b22cac51..9d5c66cac 100644 --- a/powertools-idempotency/powertools-idempotency-core/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,10 +14,13 @@ package software.amazon.lambda.powertools.idempotency; +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 java.time.Duration; +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. @@ -30,11 +33,12 @@ public class IdempotencyConfig { private final String payloadValidationJMESPath; private final boolean throwOnNoIdempotencyKey; private final String hashFunction; + private final BiFunction<Object, DataRecord, Object> responseHook; private Context lambdaContext; 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; @@ -42,6 +46,7 @@ private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESP this.payloadValidationJMESPath = payloadValidationJMESPath; this.throwOnNoIdempotencyKey = throwOnNoIdempotencyKey; this.hashFunction = hashFunction; + this.responseHook = responseHook; } /** @@ -89,6 +94,10 @@ public void setLambdaContext(Context lambdaContext) { this.lambdaContext = lambdaContext; } + public BiFunction<Object, DataRecord, Object> getResponseHook() { + return responseHook; + } + public static class Builder { private int localCacheMaxItems = 256; @@ -98,14 +107,18 @@ public static class Builder { 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> @@ -120,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> @@ -136,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) { @@ -147,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) { @@ -158,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) { @@ -170,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) { @@ -182,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) { @@ -193,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) { @@ -215,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/powertools-idempotency-core/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 index 7982d911a..38196b5d2 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -14,12 +14,21 @@ package software.amazon.lambda.powertools.idempotency.internal; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.JsonNode; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; + +import java.time.Instant; +import java.util.OptionalInt; +import java.util.function.BiFunction; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +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; @@ -32,14 +41,9 @@ import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Instant; -import java.util.OptionalInt; - -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; - /** - * 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 { @@ -106,7 +110,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 */ @@ -144,7 +149,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) { @@ -167,10 +173,25 @@ private Object handleForStatus(DataRecord record) { try { LOG.debug("Response for key '{}' retrieved from idempotency store, skipping the function", record.getIdempotencyKey()); + + final BiFunction<Object, DataRecord, Object> responseHook = Idempotency.getInstance().getConfig() + .getResponseHook(); + final Object responseData; + if (returnType.equals(String.class)) { - return record.getResponseData(); + // Primitive String data will be returned raw and not de-serialized from JSON. + responseData = record.getResponseData(); + } else { + responseData = JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), + returnType); } - 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); diff --git a/powertools-idempotency/powertools-idempotency-core/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 index e5f45aef7..a434bcdc0 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -14,15 +14,32 @@ package software.amazon.lambda.powertools.idempotency.internal; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; +import static java.time.temporal.ChronoUnit.SECONDS; +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.doReturn; +import static org.mockito.Mockito.doThrow; +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 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.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.JsonProcessingException; +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.IdempotencyConfig; @@ -43,21 +60,6 @@ import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Instant; -import java.util.OptionalInt; -import java.util.OptionalLong; - -import static java.time.temporal.ChronoUnit.SECONDS; -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.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - public class IdempotencyAspectTest { @Mock @@ -77,8 +79,8 @@ public void firstCall_shouldPutInStore() { .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -103,6 +105,45 @@ public void firstCall_shouldPutInStore() { assertThat(resultCaptor.getValue()).isEqualTo(basket); } + @Test + public void firstCall_shouldPutInStoreAndNotApplyResponseHook() { + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + // 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); // Size should be 1 because response hook should not run + 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 secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { // GIVEN @@ -110,20 +151,20 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); doThrow(IdempotencyItemAlreadyExistsException.class).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( "42", 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 IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -141,19 +182,19 @@ public void secondCall_notExpired_shouldGetStringFromStore() { .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); doThrow(IdempotencyItemAlreadyExistsException.class).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,6 +205,41 @@ public void secondCall_notExpired_shouldGetStringFromStore() { assertThat(function.handlerCalled()).isFalse(); } + @Test + public 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(IdempotencyItemAlreadyExistsException.class).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 public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() throws JsonProcessingException { @@ -172,23 +248,23 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); doThrow(IdempotencyItemAlreadyExistsException.class).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(); @@ -204,23 +280,23 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); doThrow(IdempotencyItemAlreadyExistsException.class).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(); @@ -235,8 +311,8 @@ public void functionThrowException_shouldDeleteRecord_andThrowFunctionException( .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); // WHEN / THEN IdempotencyWithErrorFunction function = new IdempotencyWithErrorFunction(); @@ -256,8 +332,8 @@ public void testIdempotencyDisabled_shouldJustRunTheFunction() { .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - .build() - ).configure(); + .build()) + .configure(); // WHEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -340,13 +416,13 @@ public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromS 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); @@ -405,8 +481,7 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPathArray_shouldPutInStoreWit public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { Idempotency.config() .withPersistenceStore(store) - .withConfig(IdempotencyConfig.builder().build() - ).configure(); + .withConfig(IdempotencyConfig.builder().build()).configure(); // WHEN IdempotencyInternalFunctionInvalid function = new IdempotencyInternalFunctionInvalid(); @@ -421,8 +496,7 @@ public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { public void idempotencyOnSubMethodVoid_shouldThrowException() { Idempotency.config() .withPersistenceStore(store) - .withConfig(IdempotencyConfig.builder().build() - ).configure(); + .withConfig(IdempotencyConfig.builder().build()).configure(); // WHEN IdempotencyInternalFunctionVoid function = new IdempotencyInternalFunctionVoid(); From 7f604e4c67d838d6f72ad340c5f4af7492f1e624 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 7 Apr 2025 10:48:27 +0200 Subject: [PATCH 0645/1008] ci: Fix infrastructure deployment and assertions in end-to-end tests. (#1816) * Fix CDK Stack creation in e2e tests. * Fix timezone problem in MetricsE2ET.java. * Fix tracing E2E tests. * Consistently use UTC time in MetricsE2ET. * Fix LargeMessageE2ET. Initialize ExtendedSQSClient with correct region and use Autoclosable. --- .../lambda/powertools/e2e/Function.java | 2 +- powertools-e2e-tests/pom.xml | 6 +- .../lambda/powertools/LargeMessageE2ET.java | 99 ++++++++++--------- .../amazon/lambda/powertools/MetricsE2ET.java | 67 ++++++------- .../amazon/lambda/powertools/TracingE2ET.java | 66 ++++++++----- .../powertools/testutils/Infrastructure.java | 99 ++++++++++--------- .../testutils/metrics/MetricsFetcher.java | 13 ++- 7 files changed, 186 insertions(+), 166 deletions(-) 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/pom.xml b/powertools-e2e-tests/pom.xml index 1bf6310f4..03b6fe413 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -30,8 +30,8 @@ <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <constructs.version>10.3.0</constructs.version> - <cdk.version>2.162.1</cdk.version> + <constructs.version>10.4.2</constructs.version> + <cdk.version>2.186.0</cdk.version> </properties> <dependencies> @@ -231,4 +231,4 @@ </profile> </profiles> -</project> \ No newline at end of file +</project> 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..d9c3ef749 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,6 +12,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.AfterEach; @@ -22,6 +21,10 @@ import org.junit.jupiter.api.Timeout; 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; @@ -101,21 +104,20 @@ 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()); - + 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()); + } Thread.sleep(30000); // wait for function to be executed // then @@ -137,36 +139,37 @@ public void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException, In @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"); + 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()); + + 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"); + } } } 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 235255dff..2765e0e70 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,9 +18,8 @@ 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.LocalDateTime; -import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.List; @@ -29,10 +28,12 @@ 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.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; import software.amazon.lambda.powertools.testutils.metrics.MetricsFetcher; @@ -51,9 +52,9 @@ public static void setup() { .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(); @@ -71,14 +72,10 @@ public static void tearDown() { public void test_recordMetrics() { // GIVEN - Instant currentTimeTruncatedToMinutes = - LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).toInstant(ZoneOffset.UTC); + Instant currentTimeTruncatedToMinutes = Instant.now(Clock.systemUTC()).truncatedTo(ChronoUnit.MINUTES); + String event1 = "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"}, \"highResolution\": \"false\"}"; - String event1 = - "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"}, \"highResolution\": \"false\"}"; - - String event2 = - "{ \"metrics\": {\"orders\": 1, \"products\": 8}, \"dimensions\": { \"Environment\": \"test\"}, \"highResolution\": \"true\"}"; + String event2 = "{ \"metrics\": {\"orders\": 1, \"products\": 8}, \"dimensions\": { \"Environment\": \"test\"}, \"highResolution\": \"true\"}"; // WHEN InvocationResult invocationResult = invokeFunction(functionName, event1); @@ -86,45 +83,43 @@ public void test_recordMetrics() { // 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")); + List<Double> orderMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), + 60, namespace, + "orders", Collections.singletonMap("Environment", "test")); assertThat(orderMetrics.get(0)).isEqualTo(2); - List<Double> productMetrics = - metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, - "products", Collections.singletonMap("Environment", "test")); + 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 + // 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)); + 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)); + 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")); + 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 + // 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/TracingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java index 0827d91ae..d2a5ceed1 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 @@ -18,14 +18,15 @@ 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; @@ -45,7 +46,9 @@ public static void setup() { .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); @@ -61,45 +64,58 @@ public static void tearDown() { @Test public 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) .build() .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); + assertThat(trace.getSubsegments()).hasSize(2); + + // We need to filter segments based on name because they are not returned in-order from the X-Ray API + // The Init segment is created by default for Lambda functions in X-Ray + final SubSegment initSegment = trace.getSubsegments().stream() + .filter(subSegment -> subSegment.getName().equals("Init")) + .findFirst().orElse(null); + assertThat(initSegment.getName()).isEqualTo("Init"); + assertThat(initSegment.getAnnotations()).isNull(); + + final SubSegment handleRequestSegment = trace.getSubsegments().stream() + .filter(subSegment -> subSegment.getName().equals("## handleRequest")) + .findFirst().orElse(null); + 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 -> subSegment.getName().equals("## buildMessage")) + .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/testutils/Infrastructure.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java index bce4bbf98..143409989 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,7 +16,6 @@ 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; @@ -27,13 +26,18 @@ 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.DefaultStackSynthesizer; import software.amazon.awscdk.DockerVolume; import software.amazon.awscdk.Duration; import software.amazon.awscdk.RemovalPolicy; @@ -45,7 +49,11 @@ 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; @@ -89,7 +97,8 @@ * 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 { @@ -174,11 +183,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,7 +212,11 @@ public void destroy() { */ private Stack createStackWithLambda() { boolean createTableForAsyncTests = false; - Stack stack = new Stack(app, stackName); + final Stack e2eStack = Stack.Builder.create(app, stackName) + .synthesizer(DefaultStackSynthesizer.Builder.create() + .generateBootstrapVersionRule(false) // Disable bootstrap version check + .build()) + .build(); List<String> packagingInstruction = Arrays.asList( "/bin/sh", @@ -213,8 +226,7 @@ private Stack createStackWithLambda() { " -Dmaven.test.skip=true " + " -Dmaven.compiler.source=" + runtime.getMvnProperty() + " -Dmaven.compiler.target=" + runtime.getMvnProperty() + - " && cp /asset-input/" + pathToFunction + "/target/function.jar /asset-output/" - ); + " && cp /asset-input/" + pathToFunction + "/target/function.jar /asset-output/"); BundlingOptions.Builder builderOptions = BundlingOptions.builder() .command(packagingInstruction) @@ -224,20 +236,19 @@ private Stack createStackWithLambda() { DockerVolume.builder() .hostPath(System.getProperty("user.home") + "/.m2/") .containerPath("/root/.m2/") - .build() - )) + .build())) .user("root") .outputType(BundlingOutput.ARCHIVED); functionName = stackName + "-function"; - CfnOutput.Builder.create(stack, FUNCTION_NAME_OUTPUT) + CfnOutput.Builder.create(e2eStack, FUNCTION_NAME_OUTPUT) .value(functionName) .build(); LOG.debug("Building Lambda function with command " + packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); Function function = Function.Builder - .create(stack, functionName) + .create(e2eStack, functionName) .code(Code.fromAsset("handlers/", AssetOptions.builder() .bundling(builderOptions .command(packagingInstruction) @@ -253,7 +264,7 @@ private Stack createStackWithLambda() { .build(); LogGroup.Builder - .create(stack, functionName + "-logs") + .create(e2eStack, functionName + "-logs") .logGroupName("/aws/lambda/" + functionName) .retention(RetentionDays.ONE_DAY) .removalPolicy(RemovalPolicy.DESTROY) @@ -261,7 +272,7 @@ private Stack createStackWithLambda() { 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,7 +286,7 @@ 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)) @@ -293,14 +304,14 @@ 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) .build(); @@ -316,13 +327,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 +347,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 +363,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 +388,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 +408,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 +425,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 +434,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,13 +454,13 @@ private void synthesize() { */ private void uploadAssets() { Map<String, Asset> assets = findAssets(); - assets.forEach((objectKey, asset) -> - { + assets.forEach((objectKey, asset) -> { if (!asset.assetPath.endsWith(".jar")) { return; } - ListObjectsV2Response objects = - s3.listObjectsV2(ListObjectsV2Request.builder().bucket(asset.bucketName).build()); + + 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"); return; @@ -472,14 +482,13 @@ 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(); + 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(); Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account) .replace("${AWS::Region}", region.toString())); assets.put(objectKey, asset); @@ -509,8 +518,6 @@ private Builder() { runtime = mapRuntimeVersion("JAVA_VERSION"); } - - private JavaRuntime mapRuntimeVersion(String environmentVariableName) { String javaVersion = System.getenv(environmentVariableName); // must be set in GitHub actions JavaRuntime ret = null; 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..186e72d13 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 @@ -54,7 +54,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,14 +67,13 @@ 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 = () -> - { + Callable<List<Double>> callable = () -> { LOG.debug("Get Metrics for namespace {}, start {}, end {}, metric {}, dimensions {}", namespace, start, end, metricName, dimensionsList); GetMetricDataResponse metricData = cloudwatch.getMetricData(GetMetricDataRequest.builder() @@ -109,9 +109,8 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String .build(); CallExecutor<List<Double>> callExecutor = new CallExecutorBuilder<List<Double>>() .config(retryConfig) - .afterFailedTryListener(s -> - { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + .afterFailedTryListener(s -> { + LOG.warn("{}, attempts: {}", s.getLastExceptionThatCausedRetry().getMessage(), s.getTotalTries()); }) .build(); Status<List<Double>> status = callExecutor.execute(callable); From 98b36b5da4250ed6f88a770a2c753ebe59bda48f Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 15 Apr 2025 11:10:24 +0200 Subject: [PATCH 0646/1008] docs: v2 documentation maintenance fixing formatting and dependency issues as well as adding roadmap and llms.txt (#1819) * Update docs requirements using pip-compile. Add privacy plugin. Add llms.txt plugin. Make Idempotency documentation environment variable naming consistent. Fix documentation formatting issue in FAQ.md. * Add roadmap. * Fix outdated AspectJ configuration in documentation pages. * Synchronize Changelog with recent releases. * Add llms.txt to links to navbar. --- .gitignore | 4 +- CHANGELOG.md | 76 ++++++++ README.md | 28 +-- docs/Dockerfile | 6 +- docs/FAQs.md | 66 +++---- docs/core/logging.md | 20 ++- docs/core/metrics.md | 10 +- docs/core/tracing.md | 10 +- docs/index.md | 24 ++- docs/requirements.in | 3 + docs/requirements.txt | 296 +++++++++++++++++++++++++++++++ docs/roadmap.md | 141 +++++++++++++++ docs/utilities/idempotency.md | 24 ++- docs/utilities/large_messages.md | 12 +- docs/utilities/parameters.md | 12 +- docs/utilities/validation.md | 10 +- mkdocs.yml | 32 +++- 17 files changed, 704 insertions(+), 70 deletions(-) create mode 100644 docs/requirements.in create mode 100644 docs/requirements.txt create mode 100644 docs/roadmap.md diff --git a/.gitignore b/.gitignore index 6615ac729..0972d7449 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/ @@ -110,4 +110,4 @@ example/HelloWorldFunction/build .gradle build/ .terraform* -terraform.tfstate* \ No newline at end of file +terraform.tfstate* diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b9f664fe..20a04c488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,82 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.20.1] - 2025-04-08 + +* docs: fix 2 typos (#1739) by @ntestor +* docs: Correct XML formatting for Maven configuration in Large Messages utility docs (#1796) by @jreijn +* fix: Load version.properties file as resource stream to fix loading when packaged as jar (#1813) by @phipag + +## [1.20.0] - 2025-03-25 + +* feat(cfn-custom-resource): Add optional 'reason' field for detailed failure reporting (#1758) by @moizsh + +## [1.19.0] - 2025-03-07 + +* chore(deps): Update deps for jackson ([#1793](https://github.com/aws-powertools/powertools-lambda-java/pull/1793)) by [@sthulb](https://github.com/sthulb) +* build(deps): bump log4j.version from 2.22.1 to 2.24.3 ([#1777](https://github.com/aws-powertools/powertools-lambda-java/pull/1777)) by [@dependabot](https://github.com/dependabot) +* chore(deps): update JSII to 1.108 ([#1791](https://github.com/aws-powertools/powertools-lambda-java/pull/1791)) by [@sthulb](https://github.com/sthulb) +* build(deps): bump jinja2 from 3.1.5 to 3.1.6 in /docs ([#1789](https://github.com/aws-powertools/powertools-lambda-java/pull/1789)) by [@dependabot](https://github.com/dependabot) +* chore: Update netty version ([#1768](https://github.com/aws-powertools/powertools-lambda-java/pull/1768)) by [@sthulb](https://github.com/sthulb) +* chore: Set versions of transitive dependencies ([#1767](https://github.com/aws-powertools/powertools-lambda-java/pull/1767)) by [@sthulb](https://github.com/sthulb) +* chore: update Jackson in examples ([#1766](https://github.com/aws-powertools/powertools-lambda-java/pull/1766)) by [@sthulb](https://github.com/sthulb) +* build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.4.1 to 3.4.2 ([#1731](https://github.com/aws-powertools/powertools-lambda-java/pull/1731)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.xray.recorder.version from 2.15.3 to 2.18.1 ([#1726](https://github.com/aws-powertools/powertools-lambda-java/pull/1726)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.26.29 to 2.27.12 ([#1724](https://github.com/aws-powertools/powertools-lambda-java/pull/1724)) by [@dependabot](https://github.com/dependabot) +* fix: Allow empty responses as well as null response in AppConfig ([#1673](https://github.com/aws-powertools/powertools-lambda-java/pull/1673)) by [@chrisclayson](https://github.com/chrisclayson) +* build(deps): bump aws.sdk.version from 2.27.2 to 2.27.7 ([#1715](https://github.com/aws-powertools/powertools-lambda-java/pull/1715)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.26.29 to 2.27.2 ([#1714](https://github.com/aws-powertools/powertools-lambda-java/pull/1714)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.26 to 2.26.29 ([#1713](https://github.com/aws-powertools/powertools-lambda-java/pull/1713)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.26.25 to 2.26.29 ([#1712](https://github.com/aws-powertools/powertools-lambda-java/pull/1712)) by [@dependabot](https://github.com/dependabot) +* chore: deprecate java1.8 al1 ([#1706](https://github.com/aws-powertools/powertools-lambda-java/pull/1706)) by [@jeromevdl](https://github.com/jeromevdl) +* chore: java 1.8 AL1 is deprecated, fix E2E tests ([#1692](https://github.com/aws-powertools/powertools-lambda-java/pull/1692)) by [@jeromevdl](https://github.com/jeromevdl) +* build(deps): bump aws.sdk.version from 2.26.21 to 2.26.25 ([#1703](https://github.com/aws-powertools/powertools-lambda-java/pull/1703)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.26.3 to 2.26.21 ([#1697](https://github.com/aws-powertools/powertools-lambda-java/pull/1697)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump jackson.version from 2.17.0 to 2.17.2 ([#1696](https://github.com/aws-powertools/powertools-lambda-java/pull/1696)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.commons:commons-lang3 from 3.13.0 to 3.14.0 ([#1694](https://github.com/aws-powertools/powertools-lambda-java/pull/1694)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump commons-io:commons-io from 2.15.1 to 2.16.1 ([#1691](https://github.com/aws-powertools/powertools-lambda-java/pull/1691)) by [@dependabot](https://github.com/dependabot) +* docs: improve tracing doc for sdk instrumentation ([#1687](https://github.com/aws-powertools/powertools-lambda-java/pull/1687)) by [@jeromevdl](https://github.com/jeromevdl) +* docs: fix tracing links for xray ([#1686](https://github.com/aws-powertools/powertools-lambda-java/pull/1686)) by [@jeromevdl](https://github.com/jeromevdl) +* build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.5 to 3.3.0 ([#1679](https://github.com/aws-powertools/powertools-lambda-java/pull/1679)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.69 to 2.26.3 ([#1658](https://github.com/aws-powertools/powertools-lambda-java/pull/1658)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.7.3.6 to 4.8.5.0 ([#1657](https://github.com/aws-powertools/powertools-lambda-java/pull/1657)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.3.0 to 3.4.0 ([#1653](https://github.com/aws-powertools/powertools-lambda-java/pull/1653)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.50 to 2.25.69 ([#1652](https://github.com/aws-powertools/powertools-lambda-java/pull/1652)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1 ([#1646](https://github.com/aws-powertools/powertools-lambda-java/pull/1646)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.assertj:assertj-core from 3.25.3 to 3.26.0 ([#1644](https://github.com/aws-powertools/powertools-lambda-java/pull/1644)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.xray.recorder.version from 2.15.1 to 2.15.3 ([#1643](https://github.com/aws-powertools/powertools-lambda-java/pull/1643)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.35 to 2.25.50 ([#1642](https://github.com/aws-powertools/powertools-lambda-java/pull/1642)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump com.amazonaws:aws-lambda-java-events from 3.11.2 to 3.11.4 ([#1597](https://github.com/aws-powertools/powertools-lambda-java/pull/1597)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.24.10 to 2.25.6 ([#1603](https://github.com/aws-powertools/powertools-lambda-java/pull/1603)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.5 ([#1596](https://github.com/aws-powertools/powertools-lambda-java/pull/1596)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.2.0 ([#1585](https://github.com/aws-powertools/powertools-lambda-java/pull/1585)) by [@dependabot](https://github.com/dependabot) +* build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib from 2.100.0 to 2.130.0 ([#1586](https://github.com/aws-powertools/powertools-lambda-java/pull/1586)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump io.burt:jmespath-jackson from 0.5.1 to 0.6.0 ([#1587](https://github.com/aws-powertools/powertools-lambda-java/pull/1587)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.21.0 to 2.24.10 ([#1581](https://github.com/aws-powertools/powertools-lambda-java/pull/1581)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump commons-io:commons-io from 2.13.0 to 2.15.1 ([#1584](https://github.com/aws-powertools/powertools-lambda-java/pull/1584)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.xray.recorder.version from 2.14.0 to 2.15.1 ([#1583](https://github.com/aws-powertools/powertools-lambda-java/pull/1583)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-shade-plugin from 3.5.0 to 3.5.2 ([#1582](https://github.com/aws-powertools/powertools-lambda-java/pull/1582)) by [@dependabot](https://github.com/dependabot) +* build(deps-dev): bump org.yaml:snakeyaml from 2.1 to 2.2 ([#1400](https://github.com/aws-powertools/powertools-lambda-java/pull/1400)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump log4j.version from 2.20.0 to 2.22.1 ([#1547](https://github.com/aws-powertools/powertools-lambda-java/pull/1547)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-artifact-plugin from 3.4.1 to 3.5.0 ([#1485](https://github.com/aws-powertools/powertools-lambda-java/pull/1485)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump com.amazonaws:aws-lambda-java-serialization from 1.1.2 to 1.1.5 ([#1573](https://github.com/aws-powertools/powertools-lambda-java/pull/1573)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.10 to 0.8.11 ([#1509](https://github.com/aws-powertools/powertools-lambda-java/pull/1509)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aspectj to 1.9.21 for jdk21 ([#1536](https://github.com/aws-powertools/powertools-lambda-java/pull/1536)) by [@jeromevdl](https://github.com/jeromevdl) +* docs: HelloWorldStreamFunction in examples fails with sam ([#1532](https://github.com/aws-powertools/powertools-lambda-java/pull/1532)) by [@jasoniharris](https://github.com/jasoniharris) +* chore: Testing java21 aspectj pre-release ([#1519](https://github.com/aws-powertools/powertools-lambda-java/pull/1519)) by [@scottgerring](https://github.com/scottgerring) +* fix: LargeMessageIdempotentE2ET Flaky ([#1518](https://github.com/aws-powertools/powertools-lambda-java/pull/1518)) by [@scottgerring](https://github.com/scottgerring) +* build(deps): bump software.amazon.payloadoffloading:payloadoffloading-common from 2.1.3 to 2.2.0 ([#1639](https://github.com/aws-powertools/powertools-lambda-java/pull/1639)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.3.0 to 3.4.1 ([#1638](https://github.com/aws-powertools/powertools-lambda-java/pull/1638)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump jackson.version from 2.15.3 to 2.17.0 ([#1637](https://github.com/aws-powertools/powertools-lambda-java/pull/1637)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.31 to 2.25.35 ([#1629](https://github.com/aws-powertools/powertools-lambda-java/pull/1629)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.16 to 2.25.31 ([#1625](https://github.com/aws-powertools/powertools-lambda-java/pull/1625)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.21.1 to 2.25.26 ([#1622](https://github.com/aws-powertools/powertools-lambda-java/pull/1622)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.1.2 to 3.2.5 ([#1619](https://github.com/aws-powertools/powertools-lambda-java/pull/1619)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump com.fasterxml.jackson.datatype:jackson-datatype-joda from 2.15.2 to 2.17.0 ([#1616](https://github.com/aws-powertools/powertools-lambda-java/pull/1616)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump aws.sdk.version from 2.25.6 to 2.25.16 ([#1613](https://github.com/aws-powertools/powertools-lambda-java/pull/1613)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.1.0 to 3.2.1 ([#1610](https://github.com/aws-powertools/powertools-lambda-java/pull/1610)) by [@dependabot](https://github.com/dependabot) +* build(deps): bump org.assertj:assertj-core from 3.24.2 to 3.25.3 ([#1609](https://github.com/aws-powertools/powertools-lambda-java/pull/1609)) by [@dependabot](https://github.com/dependabot) + ## [1.18.0] - 2023-11-16 ### Added diff --git a/README.md b/README.md index bf04b54b1..fd72ee148 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless > 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/preview)** | **[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/)** ## Installation @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.18.0</version> + <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>1.18.0</version> + <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.18.0</version> + <version>2.0.0-SNAPSHOT</version> </dependency> ... </dependencies> @@ -50,7 +50,7 @@ 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>11</source> <target>11</target> @@ -70,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> @@ -108,7 +116,7 @@ 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 "org.aspectj:aspectjrt:1.9.8.RC3" + implementation "org.aspectj:aspectjrt:1.9.22" } sourceCompatibility = 11 @@ -134,10 +142,10 @@ You may need to add the good version of `aspectjrt` to your dependencies based o <details> <summary><b>JDK - aspectj dependency matrix</b></summary> -| JDK version | aspectj version | -|-------------|-----------------| -| `11-17` | `1.9.20.1` | -| `21` | `1.9.21` | +| JDK version | aspectj version | +|-------------|------------------------| +| `11-17` | `1.9.20.1` (or higher) | +| `21` | `1.9.21` (or higher) | More info [here](https://github.com/aws-powertools/powertools-lambda-java/pull/1519/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R191). diff --git a/docs/Dockerfile b/docs/Dockerfile index 1524933ab..8b5e5e275 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:23b69789b1dd836c53ea25b32f62ef8e1a23366037acd07c90959a219fd1f285 + +COPY requirements.txt /tmp/ +RUN pip install --require-hashes -r /tmp/requirements.txt diff --git a/docs/FAQs.md b/docs/FAQs.md index b42302239..f3fd2514d 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -79,7 +79,6 @@ Instead, set a specific classifier of the `aws-crt` to use the one for your targ 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> @@ -108,39 +107,40 @@ Depending on the Powertools module, there is a different way to configure the SD The following example shows how to use the Lambda Powertools Parameters module while leveraging the AWS CRT Client. - ```java hl_lines="11-16 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.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; - } +```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 Lambda Powertools for 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`. diff --git a/docs/core/logging.md b/docs/core/logging.md index b5dd38ccf..e3b0ebdb3 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -45,7 +45,7 @@ to weave the code and make sure the annotation is processed. <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 --> @@ -57,6 +57,14 @@ to weave the code and make sure the annotation is processed. </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> @@ -90,7 +98,7 @@ to weave the code and make sure the annotation is processed. <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 --> @@ -102,6 +110,14 @@ to weave the code and make sure the annotation is processed. </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> diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 165c26e1c..6083d935a 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -48,7 +48,7 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar <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 --> @@ -60,6 +60,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> diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 7ad896462..ea3174fba 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -37,7 +37,7 @@ a provides functionality to reduce the overhead of performing common tracing tas <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 --> @@ -49,6 +49,14 @@ a provides functionality to reduce the overhead of performing common tracing tas </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> diff --git a/docs/index.md b/docs/index.md index 9092adc09..ef30b4197 100644 --- a/docs/index.md +++ b/docs/index.md @@ -106,6 +106,12 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo <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> ... @@ -116,7 +122,7 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo <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 --> @@ -136,6 +142,14 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo </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> @@ -211,10 +225,10 @@ You may need to add the good version of `aspectjrt` to your dependencies based o Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md) between this library and the JDK: -| JDK version | aspectj version | -|-------------|-----------------| -| `11-17` | `1.9.20.1` | -| `21` | `1.9.21` | +| JDK version | aspectj version | +|-------------|------------------------| +| `11-17` | `1.9.20.1` (or higher) | +| `21` | `1.9.21` (or higher) | ## Environment variables 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..4a86f1437 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,141 @@ +--- +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. + +- [ ] Implement GitHub workflows 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 + +- [ ] [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"} + +##### 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/utilities/idempotency.md b/docs/utilities/idempotency.md index a6da0e37e..3953949d1 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -47,7 +47,7 @@ times with the same parameters**. This makes idempotent operations safe to retry <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 --> @@ -59,6 +59,14 @@ times with the same parameters**. This makes idempotent operations safe to retry </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> @@ -136,7 +144,7 @@ Resources: TableName: !Ref IdempotencyTable Environment: Variables: - IDEMPOTENCY_TABLE: !Ref IdempotencyTable + TABLE_NAME: !Ref IdempotencyTable ``` !!! warning "Warning: Large responses with DynamoDB persistence layer" @@ -429,7 +437,7 @@ To prevent against extended failed retries when a [Lambda function times out](ht Idempotency.config() .withPersistenceStore( DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .withTableName(System.getenv("TABLE_NAME")) .build()) .configure(); } @@ -889,7 +897,7 @@ The example below shows how to append an HTTP header to an `APIGatewayProxyRespo .build()) .withPersistenceStore( DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .withTableName(System.getenv("TABLE_NAME")) .build()) .configure(); ``` @@ -1008,7 +1016,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> @@ -1112,7 +1120,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(); @@ -1149,7 +1157,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(); @@ -1194,7 +1202,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ ```json hl_lines="3" { "IdempotentFunction": { - "IDEMPOTENCY_TABLE_NAME": "idempotency" + "TABLE_NAME": "idempotency" } } ``` diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index 29244de98..38228afe9 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -114,7 +114,7 @@ of amazon-sns-java-extended-client-lib. <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 --> @@ -126,6 +126,14 @@ of amazon-sns-java-extended-client-lib. </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> @@ -344,4 +352,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 ab9c04c64..e5fb11800 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -54,7 +54,7 @@ Note that you must provide the concrete parameters module you want to use below <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 --> @@ -67,6 +67,14 @@ Note that you must provide the concrete parameters module you want to use below </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> @@ -604,4 +612,4 @@ You can create your own custom parameter store provider by implementing a handfu } } - ``` \ No newline at end of file + ``` diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 226e10bb6..f73f7e787 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -32,7 +32,7 @@ This utility provides JSON Schema validation for payloads held within events and <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 --> @@ -44,6 +44,14 @@ This utility provides JSON Schema validation for payloads held within events and </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> diff --git a/mkdocs.yml b/mkdocs.yml index aa7b0e314..cf73d41fc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,6 +6,7 @@ nav: - Homepage: index.md - Changelog: changelog.md - FAQs: FAQs.md + - Roadmap: roadmap.md - Core utilities: - core/logging.md - core/tracing.md @@ -20,6 +21,9 @@ nav: - utilities/serialization.md - Processes: - processes/maintainers.md + - Resources: + - "llms.txt": ./llms.txt + - "llms.txt (full version)": ./llms-full.txt theme: name: material @@ -57,8 +61,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 @@ -76,6 +82,30 @@ 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 + - 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/large_messages.md + - utilities/batch.md + - utilities/validation.md + - utilities/custom_resources.md + - utilities/serialization.md + Processes: + - processes/maintainers.md extra_css: - stylesheets/extra.css From 7b18029dff8f86cb9be6be8cbc4567aaacbec418 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 22 Apr 2025 21:43:29 +0200 Subject: [PATCH 0647/1008] feat(idempotency): Add support for ReturnValuesOnConditionCheckFailure in Idempotency. (#1821) --- ...IdempotencyItemAlreadyExistsException.java | 15 ++++++ .../internal/IdempotencyHandler.java | 8 +-- .../idempotency/persistence/DataRecord.java | 19 +++++-- .../internal/IdempotencyAspectTest.java | 51 ++++++++++++++++--- .../dynamodb/DynamoDBPersistenceStore.java | 11 +++- .../persistence/dynamodb}/DynamoDBConfig.java | 16 +++--- .../DynamoDBPersistenceStoreTest.java | 26 ++++++---- .../dynamodb}/IdempotencyTest.java | 21 ++++---- .../handlers/IdempotencyFunction.java | 24 +++++---- 9 files changed, 136 insertions(+), 55 deletions(-) rename powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/{software.amazon.lambda.powertools.idempotency.dynamodb => software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/DynamoDBConfig.java (93%) rename powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/{software.amazon.lambda.powertools.idempotency.dynamodb => software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/IdempotencyTest.java (78%) rename powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/{software.amazon.lambda.powertools.idempotency.dynamodb => software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/handlers/IdempotencyFunction.java (85%) diff --git a/powertools-idempotency/powertools-idempotency-core/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 index ba7da69bf..aed2e5ae0 100644 --- a/powertools-idempotency/powertools-idempotency-core/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/powertools-idempotency-core/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 index 38196b5d2..0466f244f 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -94,9 +94,11 @@ 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; diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 2d56fe349..0621c372b 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -53,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; @@ -63,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; @@ -131,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/powertools-idempotency-core/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 index a434bcdc0..12113fc9e 100644 --- a/powertools-idempotency/powertools-idempotency-core/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,6 +20,7 @@ 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; @@ -154,7 +155,7 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce .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); @@ -175,6 +176,44 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce assertThat(function.handlerCalled()).isFalse(); } + @Test + public 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 dr = new DataRecord( + "42", + DataRecord.Status.COMPLETED, + Instant.now().plus(356, SECONDS).getEpochSecond(), + JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), + null); + + // 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(); + Basket basket = function.handleRequest(p, context); + + // 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() { // GIVEN @@ -185,7 +224,7 @@ public void secondCall_notExpired_shouldGetStringFromStore() { .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 dr = new DataRecord( @@ -220,7 +259,7 @@ public void secondCall_notExpired_shouldGetStringFromStoreWithResponseHook() { .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 dr = new DataRecord( @@ -251,7 +290,7 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti .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); @@ -283,7 +322,7 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons .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); @@ -412,7 +451,7 @@ public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromS .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); diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java index 0e20e396f..9c96541f6 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java @@ -189,10 +189,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); } diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java similarity index 93% rename from powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java index 30b4976d7..289b0f1cd 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java @@ -12,15 +12,18 @@ * */ -package software.amazon.lambda.powertools.idempotency.dynamodb; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; -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 com.amazonaws.services.dynamodbv2.local.main.ServerRunner; +import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; + import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -66,13 +69,12 @@ public static void setupDynamo() { .tableName(TABLE_NAME) .keySchema(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("id").build()) .attributeDefinitions( - AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build() - ) + AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build()) .billingMode(BillingMode.PAY_PER_REQUEST) .build()); - DescribeTableResponse response = - client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); + DescribeTableResponse response = client + .describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); if (response == null) { throw new RuntimeException("Table was not created within expected time"); } diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java index cc682a81f..56b32c4f9 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java @@ -32,7 +32,7 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; -import software.amazon.lambda.powertools.idempotency.dynamodb.DynamoDBConfig; + import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; @@ -155,13 +155,14 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA DataRecord.Status.INPROGRESS, expiry2, null, - null - ), now) - ).isInstanceOf(IdempotencyItemAlreadyExistsException.class); + null), + 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)); @@ -190,13 +191,16 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire DataRecord.Status.INPROGRESS, expiry2, "Fake Data 2", - null - ), now)) - .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + null), + 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)); diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/IdempotencyTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java similarity index 78% rename from powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/IdempotencyTest.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java index be915b610..7b43542c8 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/IdempotencyTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java @@ -12,20 +12,21 @@ * */ -package software.amazon.lambda.powertools.idempotency.dynamodb; - +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; import static org.assertj.core.api.Assertions.assertThat; -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 com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.amazonaws.services.lambda.runtime.tests.EventLoader; + import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.lambda.powertools.idempotency.dynamodb.handlers.IdempotencyFunction; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.handlers.IdempotencyFunction; public class IdempotencyTest extends DynamoDBConfig { @@ -41,14 +42,14 @@ void setUp() { public 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/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java similarity index 85% rename from powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java index 227eea39e..d816af801 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java @@ -12,14 +12,16 @@ * */ -package software.amazon.lambda.powertools.idempotency.dynamodb.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.util.HashMap; -import java.util.Map; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; @@ -32,15 +34,15 @@ public class IdempotencyFunction implements RequestHandler<APIGatewayProxyReques 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 @@ -56,9 +58,9 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); - return response - .withStatusCode(200) - .withBody("{ \"message\": \"hello world\"}"); + return response + .withStatusCode(200) + .withBody("{ \"message\": \"hello world\"}"); } } From 374b38db3b91d421a14bb71b4df0194c72304efa Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 24 Apr 2025 17:46:16 +0200 Subject: [PATCH 0648/1008] fix(logging): Prevent accidental overwriting of reserved keys via structured arguments * Ignore logging of reserved keys when passed as StructuredArgument. * Add error to reserved log keys. * Update docs with a warning about logging of reserved keys when using structured arguments. * Update docs with warning in MDC section as well. * Update product name in documentation. * Make StructuredArgument implementations package private. They should only be instantiated by factory StructuredArguments. * Make StructuredArgument interface public and remove package private dependency in LambdaLoggingAspectTest. * Move reserved key logic from logging implementation to StructuredArguments. * Remove .keys() method again from StructuredArgument interface. It is no longer needed now. * Update documentation warnings regarding logging of reserved keys. --- docs/core/logging.md | 9 +++ .../json/resolver/PowertoolsResolver.java | 29 ++++---- .../PowertoolsResolverArgumentsTest.java | 25 ++++++- .../internal/handler/PowertoolsArguments.java | 34 +++++++-- .../powertools/logging/logback/JsonUtils.java | 3 +- .../internal/LambdaJsonEncoderTest.java | 16 ++++ .../internal/handler/PowertoolsArguments.java | 34 +++++++-- .../logging/argument/ArrayArgument.java | 4 +- .../logging/argument/JsonArgument.java | 4 +- .../logging/argument/KeyValueArgument.java | 3 +- .../logging/argument/MapArgument.java | 12 ++- .../logging/argument/StructuredArgument.java | 8 +- .../logging/argument/StructuredArguments.java | 74 +++++++++++++++++-- .../argument/StructuredArgumentsTest.java | 64 ++++++++++++++-- .../internal/LambdaLoggingAspectTest.java | 36 ++++----- 15 files changed, 286 insertions(+), 69 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index e3b0ebdb3..1160f62ff 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -354,6 +354,9 @@ Your logs will always include the following keys in your structured logging: | **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"));`) | +???+ 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. + ## Additional structured keys ### Logging Lambda context information @@ -640,6 +643,9 @@ To append additional keys in your logs, you can use the `StructuredArguments` cl } ``` +???+ 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** 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"}, @@ -650,6 +656,9 @@ Mapped Diagnostic Context (MDC) is essentially a Key-Value store. It is supporte ???+ 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 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 index c98da7833..8ada50f49 100644 --- 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 @@ -191,8 +191,8 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { Object[] arguments = logEvent.getMessage().getParameters(); if (arguments != null) { stream(arguments).filter(StructuredArgument.class::isInstance).forEach(argument -> { - serializer.writeRaw(','); try { + serializer.writeRaw(','); ((StructuredArgument) argument).writeTo(serializer); } catch (IOException e) { System.err.printf("Failed to encode log event, error: %s.%n", e.getMessage()); @@ -204,19 +204,20 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { 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 }, - { 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]))); + 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 }, + { 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) { 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 index 24014a759..463ad043d 100644 --- 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 @@ -35,6 +35,8 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.slf4j.MDC; + +import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields; import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments; @Order(2) @@ -83,9 +85,17 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains( + "\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") .contains("\"message\":\"Message body = plop and 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 @@ -107,9 +117,18 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .contains( + "\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") .contains("\"message\":\"Message body = plop and 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.\""); + }); } private void setupContext() { @@ -119,4 +138,4 @@ private void setupContext() { when(context.getMemoryLimitInMB()).thenReturn(10); when(context.getAwsRequestId()).thenReturn("RequestId"); } -} \ No newline at end of file +} 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 index 387074590..1fc235ff7 100644 --- 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 @@ -16,15 +16,21 @@ 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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; + 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> { @@ -41,11 +47,27 @@ 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))); + 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)); + 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) { 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 index b98a8eada..67d6b268d 100644 --- 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 @@ -22,6 +22,7 @@ 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; @@ -36,7 +37,7 @@ private JsonUtils() { } static void serializeTimestamp(JsonSerializer generator, long timestamp, String timestampFormat, - String timestampFormatTimezoneId, String timestampAttributeName) { + String timestampFormatTimezoneId, String timestampAttributeName) { String formattedTimestamp; if (timestampFormat == null || timestamp < 0) { formattedTimestamp = String.valueOf(timestamp); 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 index 638857cb3..9b6fb8d1c 100644 --- 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 @@ -29,6 +29,8 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.util.JSONPObject; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -126,6 +128,13 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") .contains("\"message\":\"Message body = plop and 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 @@ -150,6 +159,13 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") .contains("\"message\":\"Message body = plop and 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.\""); + }); } private final LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "message", null, null); 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 index 387074590..1fc235ff7 100644 --- 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 @@ -16,15 +16,21 @@ 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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; + 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> { @@ -41,11 +47,27 @@ 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))); + 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)); + 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) { 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 index 28b29146e..cbedbdc0f 100644 --- 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 @@ -15,12 +15,13 @@ 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...)} */ -public class ArrayArgument implements StructuredArgument { +class ArrayArgument implements StructuredArgument { private final String key; private final Object[] values; @@ -40,4 +41,5 @@ public void writeTo(JsonSerializer serializer) { 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 index e14f23788..debea18c5 100644 --- 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 @@ -15,12 +15,13 @@ 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)} */ -public class JsonArgument implements StructuredArgument { +class JsonArgument implements StructuredArgument { private final String key; private final String rawJson; @@ -39,4 +40,5 @@ public void writeTo(JsonSerializer serializer) { 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 index 569667419..54648d99e 100644 --- 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 @@ -15,12 +15,13 @@ 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)} */ -public class KeyValueArgument implements StructuredArgument { +class KeyValueArgument implements StructuredArgument { private final String key; private final Object 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 index 9a06ea095..1155fa93b 100644 --- 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 @@ -15,13 +15,15 @@ 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)} */ -public class MapArgument implements StructuredArgument { +class MapArgument implements StructuredArgument { private final Map<?, ?> map; public MapArgument(Map<?, ?> map) { @@ -35,8 +37,13 @@ public MapArgument(Map<?, ?> map) { @Override public void writeTo(JsonSerializer serializer) { if (map != null) { - for (Map.Entry<?, ?> entry : map.entrySet()) { + 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(); + } } } } @@ -45,4 +52,5 @@ public void writeTo(JsonSerializer serializer) { 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 index 21fea068d..00cc651eb 100644 --- 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 @@ -15,7 +15,9 @@ package software.amazon.lambda.powertools.logging.argument; import java.io.IOException; + import org.slf4j.Logger; + import software.amazon.lambda.powertools.logging.internal.JsonSerializer; /** @@ -26,8 +28,10 @@ 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 + * @param serializer + * the {@link JsonSerializer} to produce JSON content + * @throws IOException + * if an I/O error occurs */ void writeTo(JsonSerializer serializer) throws IOException; 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 index 8a75b3118..f56a42ea3 100644 --- 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 @@ -15,7 +15,16 @@ 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. @@ -23,54 +32,109 @@ */ 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 + * @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 + * @return a {@link MapArgument} populated with the data, with reserved keys filtered out */ public static StructuredArgument entries(Map<?, ?> map) { - return new MapArgument(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 + * @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 + * @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); } 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 index 64200e640..b478ac0f0 100644 --- 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 @@ -15,12 +15,15 @@ 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; @@ -47,8 +50,10 @@ void keyValueArgument() throws IOException { 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}]}"); + 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 @@ -64,17 +69,32 @@ void mapArgument() throws IOException { // THEN assertThat(sb.toString()) - .contains("\"nds\":{\"id\":42,\"name\":\"Nintendo DS\",\"price\":299.45}") + .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[]{ + Product[] products = new Product[] { new Product(42, "Nintendo DS", 299.45), new Product(98, "Playstation 5", 499.99) }; @@ -84,8 +104,10 @@ void arrayArgument() throws IOException { 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}]"); + 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 @@ -102,4 +124,34 @@ void jsonArgument() throws IOException { 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/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index 557c6c893..aba4664fe 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 @@ -72,7 +72,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.logging.argument.KeyValueArgument; +import software.amazon.lambda.powertools.logging.argument.StructuredArgument; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId; @@ -466,9 +466,8 @@ void shouldLogEventForHandlerWithLogEventAnnotation() { // THEN TestLogger logger = (TestLogger) PowertoolsLogEvent.getLogger(); assertThat(logger.getArguments()).hasSize(1); - KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; - assertThat(argument.getKey()).isEqualTo("event"); - assertThat(argument.getValue()).isEqualTo(listOfOneElement); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("event=" + listOfOneElement.toString()); } @Test @@ -487,15 +486,14 @@ void shouldLogEventForHandlerWhenEnvVariableSetToTrue() { requestHandler.handleRequest(message, context); // THEN - TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar)requestHandler).getLogger(); + TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar) requestHandler).getLogger(); try { assertThat(logger.getArguments()).hasSize(1); - KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; - assertThat(argument.getKey()).isEqualTo("event"); - assertThat(argument.getValue()).isEqualTo(message); + 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){ + if (logger != null) { logger.clearArguments(); } } @@ -532,9 +530,8 @@ void shouldLogEventForStreamHandler() throws IOException { TestLogger logger = (TestLogger) PowertoolsLogEventForStream.getLogger(); try { assertThat(logger.getArguments()).hasSize(1); - KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; - assertThat(argument.getKey()).isEqualTo("event"); - assertThat(argument.getValue()).isEqualTo("{\"key\":\"value\"}"); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("event={\"key\":\"value\"}"); } finally { logger.clearArguments(); } @@ -552,9 +549,8 @@ void shouldLogResponseForHandlerWithLogResponseAnnotation() { TestLogger logger = (TestLogger) PowertoolsLogResponse.getLogger(); try { assertThat(logger.getArguments()).hasSize(1); - KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; - assertThat(argument.getKey()).isEqualTo("response"); - assertThat(argument.getValue()).isEqualTo("Hola mundo"); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("response=Hola mundo"); } finally { logger.clearArguments(); } @@ -574,9 +570,8 @@ void shouldLogResponseForHandlerWhenEnvVariableSetToTrue() { TestLogger logger = (TestLogger) PowertoolsLogEnabled.getLogger(); try { assertThat(logger.getArguments()).hasSize(1); - KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; - assertThat(argument.getKey()).isEqualTo("response"); - assertThat(argument.getValue()).isEqualTo("Bonjour le monde"); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("response=Bonjour le monde"); } finally { LoggingConstants.POWERTOOLS_LOG_RESPONSE = false; logger.clearArguments(); @@ -600,9 +595,8 @@ void shouldLogResponseForStreamHandler() throws IOException { TestLogger logger = (TestLogger) PowertoolsLogResponseForStream.getLogger(); try { assertThat(logger.getArguments()).hasSize(1); - KeyValueArgument argument = (KeyValueArgument) logger.getArguments()[0]; - assertThat(argument.getKey()).isEqualTo("response"); - assertThat(argument.getValue()).isEqualTo(input); + StructuredArgument argument = (StructuredArgument) logger.getArguments()[0]; + assertThat(argument.toString()).hasToString("response=" + input); } finally { logger.clearArguments(); } From 33a81374e2ccb2cb4762630c02897a5735f6e8a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:39:36 +0200 Subject: [PATCH 0649/1008] build(deps-dev): bump org.yaml:snakeyaml from 2.2 to 2.4 (#1798) Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 2.2 to 2.4. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.4..snakeyaml-2.2) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Simon Thulbourn <sthulb@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 03b6fe413..8120e44d8 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -159,7 +159,7 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>2.2</version> + <version>2.4</version> <scope>test</scope> </dependency> <dependency> From da9f5265a5c7ef67ba2dee3edfd941a7b4d8e3ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:40:35 +0200 Subject: [PATCH 0650/1008] build(deps): bump com.amazonaws:aws-lambda-java-events (#1799) Bumps [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs) from 3.11.2 to 3.15.0. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-events dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Simon Thulbourn <sthulb@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 782162029..d2227a95b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -13,7 +13,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.11.3</lambda.events.version> + <lambda.events.version>3.15.0</lambda.events.version> <aws.sdk.version>2.28.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 22a1b0d32..82b0209f5 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.15.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 86fa52425..2db4628b4 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.15.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 0b5b06152..41b5a20a1 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.15.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 1918b63a9..7480bd013 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.15.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 2a866fa82..fd673b573 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -52,7 +52,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.15.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index cb0fc4474..5737ffdeb 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.4</version> + <version>3.15.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 2f9bf77ef..00aba4c8d 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.4</version> + <version>3.15.0</version> </dependency> </dependencies> diff --git a/pom.xml b/pom.xml index 01ca81458..5c4ad2ef6 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.11.2</lambda.events.version> + <lambda.events.version>3.15.0</lambda.events.version> <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> From 7b5983818432ae12264e687420d46e8a99c59044 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:05:52 +0200 Subject: [PATCH 0651/1008] build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin (#1751) Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.3.0 to 3.6.0. - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.0...maven-checkstyle-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Simon Thulbourn <sthulb@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c4ad2ef6..891420766 100644 --- a/pom.xml +++ b/pom.xml @@ -651,7 +651,7 @@ <!-- 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> + <version>3.6.0</version> <configuration> <propertyExpansion>basedir=${project.rootdir}</propertyExpansion> <configLocation>checkstyle.xml</configLocation> From 51d5f5dd64afe9ec9bc4ae7f34b8646935e9e504 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 7 May 2025 18:22:41 +0200 Subject: [PATCH 0652/1008] chore(automation): Update automation workflows (#1779) (#1830) * chore(automation): Update automation workflows (#1779) * Update to <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> --------- Co-authored-by: Philipp Page <pagejep@amazon.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 121 ++-- .github/ISSUE_TEMPLATE/maintenance.yml | 4 +- .github/ISSUE_TEMPLATE/rfc.md | 159 +++-- .github/ISSUE_TEMPLATE/share_your_work.yml | 2 +- .github/ISSUE_TEMPLATE/support_powertools.yml | 8 +- .github/ISSUE_TEMPLATE/tech_debt.yml | 60 ++ .github/actions/gradle/action.yml | 0 .github/actions/restore/action.yml | 0 .github/actions/seal/action.yml | 78 +++ .github/actions/version/action.yml | 53 ++ .github/branch_protection_settings/main.json | 53 ++ .github/branch_protection_settings/v2.json | 63 ++ .github/dependency-review-config.yml | 30 + .github/pmd-ruleset.xml | 644 ++++++++++++++++++ .github/workflows/build-docs.yml | 79 ++- .github/workflows/check-build.yml | 109 +++ .github/workflows/check-e2e.yml | 71 ++ .github/workflows/check-pmd.yml | 42 ++ .../{spotbugs.yml => check-spotbugs.yml} | 45 +- .github/workflows/dispatch_analytics.yml | 43 -- .github/workflows/docs.yml | 41 -- .github/workflows/post_release.js | 112 --- .github/workflows/pr_artifacts_size.yml | 64 -- .github/workflows/pr_build.yml | 93 --- .github/workflows/pr_iac_lint.yml | 50 -- .github/workflows/publish.yml | 38 -- .github/workflows/release-drafter.yml | 29 +- .github/workflows/release-prep.yml | 80 --- .github/workflows/release.yml | 289 ++++++++ .github/workflows/run-e2e-tests.yml | 58 -- .github/workflows/secure_workflows.yml | 32 - .../workflows/security-branch-protections.yml | 72 ++ .github/workflows/security-dependabot.yml | 42 ++ .../workflows/security-dependencies-check.yml | 39 ++ .github/workflows/security-osv.yml | 37 + .../cdk/app/pom.xml | 2 +- pom.xml | 21 +- 37 files changed, 1997 insertions(+), 766 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/tech_debt.yml create mode 100644 .github/actions/gradle/action.yml create mode 100644 .github/actions/restore/action.yml create mode 100644 .github/actions/seal/action.yml create mode 100644 .github/actions/version/action.yml create mode 100644 .github/branch_protection_settings/main.json create mode 100644 .github/branch_protection_settings/v2.json create mode 100644 .github/dependency-review-config.yml create mode 100644 .github/pmd-ruleset.xml create mode 100644 .github/workflows/check-build.yml create mode 100644 .github/workflows/check-e2e.yml create mode 100644 .github/workflows/check-pmd.yml rename .github/workflows/{spotbugs.yml => check-spotbugs.yml} (55%) delete mode 100644 .github/workflows/dispatch_analytics.yml delete mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/post_release.js delete mode 100644 .github/workflows/pr_artifacts_size.yml delete mode 100644 .github/workflows/pr_build.yml delete mode 100644 .github/workflows/pr_iac_lint.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release-prep.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/run-e2e-tests.yml delete mode 100644 .github/workflows/secure_workflows.yml create mode 100644 .github/workflows/security-branch-protections.yml create mode 100644 .github/workflows/security-dependabot.yml create mode 100644 .github/workflows/security-dependencies-check.yml create mode 100644 .github/workflows/security-osv.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index abd8faa56..8810605b9 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,46 +1,81 @@ ---- name: Bug report -about: Create a report to help us improve -title: '' -labels: bug, triage -assignees: '' +description: Report a reproducible bug to help us improve +title: "Bug: TITLE" +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for submitting a bug report. Please add as much information as possible to help us reproduce, and remove any potential sensitive data. ---- + Please become familiar with [our definition of bug](https://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, 1.19.0" + 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: | + --- -<!--- 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 -``` + **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/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 index 84fce71df..ae2337402 100644 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ b/.github/ISSUE_TEMPLATE/rfc.md @@ -1,52 +1,107 @@ ---- -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 +name: Request for Comments (RFC) +description: Feature design and detailed proposals +title: "RFC: TITLE" +labels: ["RFC", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for submitting a RFC. Please add as many details as possible to help further enrich this design. + - type: input + id: relation + attributes: + label: Is this related to an existing feature request or issue? + description: Please share a link, if applicable + - type: dropdown + id: area + attributes: + label: Which Powertools for AWS Lambda (Java) utility does this relate to? + options: + - Tracer + - Logger + - Metrics + - Middleware factory + - Parameters + - Batch processing + - Typing + - Validation + - Event Source Data Classes + - Parser + - Idempotency + - Feature flags + - JMESPath functions + - Other + validations: + required: true + - type: textarea + id: summary + attributes: + label: Summary + description: Please provide an overview in one or two paragraphs + validations: + required: true + - type: textarea + id: problem + attributes: + label: Use case + description: Please share the use case and motivation behind this proposal + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: Please explain the design in detail, so anyone familiar with the project could implement it + placeholder: What the user experience looks like before and after this design? + validations: + required: true + - type: textarea + id: scope + attributes: + label: Out of scope + description: Please explain what should be considered out of scope in your proposal + validations: + required: true + - type: textarea + id: challenges + attributes: + label: Potential challenges + description: Nothing is perfect. Please share what common challenges, edge cases, unresolved areas, and suggestions on how to mitigate them + validations: + required: true + - type: textarea + id: integrations + attributes: + label: Dependencies and Integrations + description: If applicable, please share whether this feature has additional dependencies, and how it might integrate with other utilities available + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: Alternative solutions + description: Please describe what alternative solutions to this use case, if any + render: markdown + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This feature request meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda/Java/#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. + + Metadata information for admin purposes, please leave them empty. + + * RFC PR: + * Approved by: '' + * Reviewed by: '' \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml index 228ee8281..01dae4fdf 100644 --- a/.github/ISSUE_TEMPLATE/share_your_work.yml +++ b/.github/ISSUE_TEMPLATE/share_your_work.yml @@ -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..8623c2b73 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"] 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/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/branch_protection_settings/main.json b/.github/branch_protection_settings/main.json new file mode 100644 index 000000000..d283b3d5f --- /dev/null +++ b/.github/branch_protection_settings/main.json @@ -0,0 +1,53 @@ +{ + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection", + "required_status_checks": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks", + "strict": true, + "contexts": [ + "SonarCloud" + ], + "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks/contexts", + "checks": [ + { + "context": "SonarCloud", + "app_id": 57789 + } + ] + }, + "required_pull_request_reviews": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_pull_request_reviews", + "dismiss_stale_reviews": false, + "require_code_owner_reviews": false, + "require_last_push_approval": false, + "required_approving_review_count": 0 + }, + "required_signatures": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_signatures", + "enabled": false + }, + "enforce_admins": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/enforce_admins", + "enabled": true + }, + "required_linear_history": { + "enabled": false + }, + "allow_force_pushes": { + "enabled": false + }, + "allow_deletions": { + "enabled": false + }, + "block_creations": { + "enabled": false + }, + "required_conversation_resolution": { + "enabled": false + }, + "lock_branch": { + "enabled": false + }, + "allow_fork_syncing": { + "enabled": false + } +} diff --git a/.github/branch_protection_settings/v2.json b/.github/branch_protection_settings/v2.json new file mode 100644 index 000000000..fb9fdebcd --- /dev/null +++ b/.github/branch_protection_settings/v2.json @@ -0,0 +1,63 @@ +{ + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection", + "required_status_checks": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_status_checks", + "strict": true, + "contexts": [], + "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_status_checks/contexts", + "checks": [] + }, + "restrictions": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions", + "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/users", + "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/teams", + "apps_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/apps", + "users": [], + "teams": [], + "apps": [] + }, + "required_pull_request_reviews": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_pull_request_reviews", + "dismiss_stale_reviews": true, + "require_code_owner_reviews": false, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "dismissal_restrictions": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions", + "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions/users", + "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions/teams", + "users": [], + "teams": [], + "apps": [] + } + }, + "required_signatures": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_signatures", + "enabled": false + }, + "enforce_admins": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/enforce_admins", + "enabled": false + }, + "required_linear_history": { + "enabled": true + }, + "allow_force_pushes": { + "enabled": false + }, + "allow_deletions": { + "enabled": false + }, + "block_creations": { + "enabled": true + }, + "required_conversation_resolution": { + "enabled": true + }, + "lock_branch": { + "enabled": false + }, + "allow_fork_syncing": { + "enabled": false + } +} diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml new file mode 100644 index 000000000..6d737ee55 --- /dev/null +++ b/.github/dependency-review-config.yml @@ -0,0 +1,30 @@ +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' +comment-summary-in-pr: on-failure \ No newline at end of file diff --git a/.github/pmd-ruleset.xml b/.github/pmd-ruleset.xml new file mode 100644 index 000000000..b93fa19b8 --- /dev/null +++ b/.github/pmd-ruleset.xml @@ -0,0 +1,644 @@ +<?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> + </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> \ No newline at end of file diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index a75c13b52..d4bf75a9d 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,37 +1,62 @@ -name: Build Docs +# Build Docs +# +# Description: +# Builds the 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: - - v2 - paths: - - 'docs/**' - - 'mkdocs.yml' - - 'Makefile' + workflow_dispatch: + inputs: + alias: + type: string + required: false + description: | + Alias to deploy the documentation into, this is mostly for testing pre-release + versions of the documentation, such as beta versions or snapshots. + + https://docs.powertools.aws.dev/lambda/java/<alias> - push: - branches: - - main - paths: - - 'docs/**' - - 'mkdocs.yml' - - 'Makefile' +name: Build Docs +run-name: Build Docs - ${{ contains(github.head_ref, 'main') && 'main' || inputs.alias }} jobs: docs: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: Docs steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Set up Python - uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 - with: - python-version: "3.8" - - name: Capture branch and tag - id: branch_name + - name: Sanity Check + if: ${{ github.head_ref != 'main' || inputs.alias == '' }} + run: + echo "::error::No buildable docs" + + - name: Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + 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 + cp -R site/* dist/ + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Deploy run: | - echo "GIT_PYTHON_REFRESH=quiet" - make build-docs-website \ No newline at end of file + aws s3 sync \ + dist \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ github.head_ref == 'main' && '' || format('{0}/', inputs.alias )}} \ No newline at end of file diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml new file mode 100644 index 000000000..cc5931d05 --- /dev/null +++ b/.github/workflows/check-build.yml @@ -0,0 +1,109 @@ +# 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: +# The matrix build for this workflow is unusual, we need to make it dyanmic since +# we need to change java versions we build for depending on the branch. + + +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-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'examples/**' + - 'pom.xml' + - 'examples/pom.xml' + - '.github/workflows/**' + push: + branches: + - main + - v2 + 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' + - 'examples/**' + - 'examples/pom.xml' + - '.github/workflows/**' + +name: Build +run-name: Build - ${{ github.event_name }} + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + build_matrix: ${{ format('{0}{1}', steps.build_matrix_v1.outputs.build_matrix, steps.build_matrix_v1.outputs.build_matrix) }} + steps: + - id: base + name: Base + run: | + echo build_version=$(test ${{ github.ref }} == "v2" && 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" + build: + runs-on: ubuntu-latest + strategy: + matrix: + java: + - 8 + - 11 + - 17 + - 21 + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Java + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + with: + distribution: corretto + java-version: ${{ matrix.java }} + cache: maven + - id: build-maven + name: Build (Maven) + if: ${{ matrix.java != '8' }} + run: | + mvn -B install --file pom.xml \ No newline at end of file diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml new file mode 100644 index 000000000..6420cd6f7 --- /dev/null +++ b/.github/workflows/check-e2e.yml @@ -0,0 +1,71 @@ +# 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 + - v2 + 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: + max-parallel: 3 + matrix: + java: + - 11 + - 17 + - 21 + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + - 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/check-pmd.yml b/.github/workflows/check-pmd.yml new file mode 100644 index 000000000..d97698af8 --- /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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Java + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.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/spotbugs.yml b/.github/workflows/check-spotbugs.yml similarity index 55% rename from .github/workflows/spotbugs.yml rename to .github/workflows/check-spotbugs.yml index 106905a70..0749dfaa0 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -1,41 +1,50 @@ -name: SpotBugs - +# 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: - v2 paths: - - 'powertools-cloudformation/**' + - 'powertools-batch/**' - 'powertools-core/**' - - 'powertools-serialization/**' + - '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/**' - - 'powertools-parameters/**' - - 'powertools-idempotency/**' - - 'powertools-metrics/**' - '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@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java JDK 11 + - name: Setup Java uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - java-version: 11 - # 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 + 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 \ No newline at end of file diff --git a/.github/workflows/dispatch_analytics.yml b/.github/workflows/dispatch_analytics.yml deleted file mode 100644 index c93cb5b36..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@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 - 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 5e37c5f45..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@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Set up Python - uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 - 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/pr_artifacts_size.yml b/.github/workflows/pr_artifacts_size.yml deleted file mode 100644 index c4d29205b..000000000 --- a/.github/workflows/pr_artifacts_size.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Artifacts Size - -on: - pull_request: - branches: - - v2 - paths: - - 'powertools-batch/**' - - 'powertools-cloudformation/**' - - 'powertools-common/**' - - 'powertools-e2e-tests/**' - - 'powertools-idempotency-core/**' - - 'powertools-idempotency-dynamodb/**' - - 'powertools-large-messages/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'pom.xml' - - '.github/workflows/pr_artifacts_size.yml' -jobs: - codecheck: - runs-on: ubuntu-latest - permissions: - pull-requests: write - issues: read - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java JDK 11 - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: 11 - - name: Build with Maven - run: mvn clean package --file pom.xml -DskipTests artifact:buildinfo -pl '!software.amazon.lambda.examples:powertools-examples-idempotency,!software.amazon.lambda.examples:powertools-examples-batch,!software.amazon.lambda.examples:powertools-examples-cloudformation,!software.amazon.lambda.examples:powertools-examples-core-utilities-cdk,!software.amazon.lambda.examples:powertools-examples-core-utilities-sam,!software.amazon.lambda.examples:powertools-examples-core-utilities-serverless,!software.amazon.lambda.examples:powertools-examples-core-utilities-terraform,!software.amazon.lambda.examples:powertools-examples-parameters,!software.amazon.lambda.examples:powertools-examples-serialization,!software.amazon.lambda.examples:powertools-examples-validation,!software.amazon.lambda.examples:cdk,!software.amazon.lambda:powertools-examples' - - name: Get artifacts size & build report - id: artifacts-size-report - run: | - echo '## :floppy_disk: Artifacts Size Report' > report.md - echo '| Module | Version | Size (KB) |' >> report.md - echo '| --- | --- | --- |' >> report.md - artifact_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) - for artifact in $(cat target/powertools-parent-*.buildinfo | grep 'outputs.*.jar' | grep -v 'sources.jar'); do - artifact_name=$(echo "$artifact" | cut -d '=' -f2) - artifact_name=${artifact_name%-$artifact_version.jar} - artifact_size=$(grep "${artifact%%.filename*}.length" target/powertools-parent-*.buildinfo | cut -d '=' -f2) - printf "| %s | %s | %.2f |\n" "$artifact_name" "$artifact_version" "$(bc <<< "scale=2; $artifact_size/1000")" >> report.md - done - - name: Find potential existing report - uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # 2.4.0 - id: find-comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: Artifacts Size Report - - name: Write artifacts size report in comment - uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # 3.0.2 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body-path: 'report.md' - edit-mode: replace diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml deleted file mode 100644 index 2079bcb6b..000000000 --- a/.github/workflows/pr_build.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Build - -on: - pull_request: - branches: - - v2 - paths: - - 'powertools-batch/**' - - 'powertools-cloudformation/**' - - 'powertools-common/**' - - 'powertools-e2e-tests/**' - - 'powertools-idempotency/**' - - 'powertools-large-messages/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'examples/**' - - 'pom.xml' - - 'examples/pom.xml' - - '.github/workflows/**' - push: - branches: - - v2 - paths: - - 'powertools-batch/**' - - 'powertools-cloudformation/**' - - 'powertools-common/**' - - 'powertools-e2e-tests/**' - - 'powertools-idempotency/**' - - 'powertools-large-messages/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'examples/**' - - 'pom.xml' - - 'examples/pom.xml' - - '.github/workflows/**' -jobs: - build-corretto: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - java: [11, 17, 21] - name: Java ${{ matrix.java }} - env: - JAVA: ${{ matrix.java }} - AWS_REGION: eu-west-1 - permissions: - id-token: write # needed to interact with GitHub's OIDC Token endpoint. - contents: read - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: ${{ matrix.java }} - cache: 'maven' - - name: Build with Maven - run: mvn -B install --file pom.xml - - name: Build Gradle Example - Java - working-directory: examples/powertools-examples-core-utilities/gradle - run: ./gradlew build - - name: Build Gradle Example - Kotlin - working-directory: examples/powertools-examples-core-utilities/kotlin - run: ./gradlew build - - name: Upload coverage to Codecov - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.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,./powertools-large-messages/target/site/jacoco/jacoco.xml,./powertools-batch/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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - name: Upload artifact - with: - name: pr - path: pr/ diff --git a/.github/workflows/pr_iac_lint.yml b/.github/workflows/pr_iac_lint.yml deleted file mode 100644 index 531ccbbcb..000000000 --- a/.github/workflows/pr_iac_lint.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Validate IaC - -on: - push: - branches: - - v2 - pull_request: - branches: - - v2 - paths: - - 'examples/**' -jobs: - linter: - runs-on: ubuntu-latest - strategy: - matrix: - project: ["sam", "gradle", "kotlin"] - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java JDK - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: 11 - - name: Build Project - working-directory: . - run: | - mvn install -DskipTests - - name: Run SAM validator to check syntax of IaC templates - Java - working-directory: examples/powertools-examples-core-utilities//${{ matrix.project }} - run: | - sam build - sam validate --lint - - name: Setup Terraform - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - - name: Run Terraform validator to check syntax of IaC templates and produce a plan of changes - working-directory: examples/powertools-examples-core-utilities/terraform - run: | - mvn install - terraform -version - terraform init -backend=false - terraform validate - - name: Setup Terraform lint - uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. - working-directory: examples/powertools-examples-core-utilities/terraform - run: | - tflint --version - tflint --init - tflint -f compact \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 68c4d2e52..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,38 +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@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Set up Maven Central Repository - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: 11 - server-id: ossrh - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - # TODO: use environments https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment - 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.SNAPSHOT_PUBLISH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.SNAPSHOT_PUBLISH_PASSWORD }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - name: Close issues related to this release - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 - 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 72bd5c24f..f727ee25d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,15 +1,32 @@ -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: +<<<<<<< HEAD update_release_draft: runs-on: ubuntu-latest +======= + update_release: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write +>>>>>>> 4a17172a (chore(automation): Update automation workflows (#1779)) steps: - - uses: release-drafter/release-drafter@569eb7ee3a85817ab916c8f8ff03a5bd96c9c83e # v5.23.0 + - 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 f7a3c74c0..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@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - 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@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 - 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@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 - 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@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 - 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@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 - 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@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 - 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@f485fdc3f67a6d87ae6e3d11e41f648c26d7aee3 # v2.0.0 - 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@18f7dc018cc2cd597073088f7c7591b9d1c02672 # v3.14.0 - 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..3b650f105 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,289 @@ +# 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_v1.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 }} == "v2" && 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - 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@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + with: + if-no-files-found: error + name: source + path: | + * + !.git/* + include-hidden-files: true + retention-days: 1 + + quality: + runs-on: ubuntu-latest + 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@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + 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: ubuntu-latest + needs: + - setup + - quality + - version_seal + strategy: + matrix: + java: ${{ fromJson(needs.setup.outputs.build_matrix) }} + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + 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: ubuntu-latest + if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' }} + needs: + - build + environment: Release + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + with: + distribution: corretto + java-version: 21 + cache: maven + gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} + gpg-passphrase: GPG_PASSPHRASE + - 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 }} + + create_pr: + runs-on: ubuntu-latest + if: ${{ inputs.snapshot == false }} + needs: + - build + - publish + permissions: + pull-requests: write + contents: write + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.RELEASE_COMMIT }} + - id: download_source + name: Download artifacts + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # 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: branch + name: Create branch + run: | + git checkout -b ci-${{ github.run_id }} + 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 }} + - id: tag + name: Create release + run: | + gh release create v${{ inputs.version }} --target $(git rev-parse HEAD) + 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: download_source + name: Download artifacts + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + with: + name: source + - name: Build + run: | + mkdir -p dist + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build + cp -R site/* dist/ + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Deploy + run: | + aws s3 sync \ + dist \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/ \ No newline at end of file diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml deleted file mode 100644 index 255c89cfe..000000000 --- a/.github/workflows/run-e2e-tests.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Run end-to-end tests - -on: - workflow_dispatch: - - push: - branches: - - v2 - paths: # add other modules when there are under e2e tests - - 'powertools-e2e-tests/**' - - 'powertools-batch/**' - - 'powertools-core/**' - - 'powertools-common/**' - - 'powertools-idempotency/**' - - 'powertools-large-message/**' - - 'powertools-logging/**' - - 'powertools-metrics/**' - - 'powertools-parameters/**' - - 'powertools-serialization/**' - - 'powertools-tracing/**' - - 'pom.xml' - - '.github/workflows/**' - - pull_request: - branches: - - v2 - paths: - - 'powertools-e2e-tests/**' - -jobs: - e2e: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - java: [ 11, 17, 21 ] - name: End-to-end tests java${{ matrix.java }} - env: - AWS_DEFAULT_REGION: eu-west-1 - JAVA_VERSION: ${{ matrix.java }} - permissions: - id-token: write # needed to interact with GitHub's OIDC Token endpoint. - contents: read - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: ${{ matrix.java }} - cache: maven - - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 - 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/secure_workflows.yml b/.github/workflows/secure_workflows.yml deleted file mode 100644 index 1430e91d6..000000000 --- a/.github/workflows/secure_workflows.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Lockdown untrusted workflows - -# PROCESS -# -# 1. Scans for any external GitHub Action being used without version pinning (@<commit-sha> vs @v3) -# 2. Scans for insecure practices for inline bash scripts (shellcheck) -# 3. Fail CI and prevent PRs to be merged if any malpractice is found - -# USAGE -# -# Always triggered on new PR, PR changes and PR merge. - - -on: - push: - paths: - - ".github/workflows/**" - pull_request: - paths: - - ".github/workflows/**" - -jobs: - enforce_pinned_workflows: - name: Harden Security - runs-on: ubuntu-latest - permissions: - contents: read # checkout code and subsequently GitHub action workflows - steps: - - name: Checkout code - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Ensure 3rd party workflows have SHA pinned - uses: zgosalvez/github-actions-ensure-sha-pinned-actions@555a30da2656b4a7cf47b107800bef097723363e # v2.1.3 diff --git a/.github/workflows/security-branch-protections.yml b/.github/workflows/security-branch-protections.yml new file mode 100644 index 000000000..dc7c06316 --- /dev/null +++ b/.github/workflows/security-branch-protections.yml @@ -0,0 +1,72 @@ +# Branch Protections +# +# Description: +# This workflow compares current security branch protections against those stored, +# if there's any changes, it'll fail the job and alert using a Slack webhook +# +# Triggers: +# - pull_request +# - branch_protection_rule +# - cron: daily at 16:40 +# +# Secrets: +# - SECURITY.BRANCH_PROTECTION_TOKEN +# - SECURITY.SLACK_WEBHOOK_URL +# +# Notes: +# Modified copy of: https://github.com/github/docs/blob/main/.github/workflows/alert-changed-branch-protections.yml + +on: + branch_protection_rule: + schedule: + - cron: '20 16 * * *' # Run daily at 16:20 UTC + pull_request: + paths: + - .github/workflows/security-branch-protections.yml + - .github/branch_protection_settings/*.json + +name: Alert Changed Branch Protections +run-name: Alert Changed Branch Protections + +permissions: + contents: read + +jobs: + check-branch-protections: + runs-on: ubuntu-latest + permissions: + contents: write + environment: Security + if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' }} + strategy: + matrix: + # List of branches we want to monitor for protection changes + branch: + - main + - v2 + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Fetch branch protections + id: fetch + env: + GH_TOKEN: ${{ secrets.BRANCH_PROTECTION_TOKEN }} + run: | + # Fetch branch protections and store them in a file + gh api /repos/${{ github.repository }}/branches/${{ matrix.branch }}/protection | jq \ + > .github/branch_protection_settings/${{ matrix.branch }}.json + - name: Compare branch protections + id: compare + run: | + git diff --quiet .github/branch_protection_settings/${{ matrix.branch }}.json \ + || echo "diff_failed=true" >> $GITHUB_ENV + - name: Send webhook + if: ${{ env.diff_failed == 'true' }} + run: | + curl -X POST -d '{"message": "Branch protections have changed for ${{ github.repository }} on ${{ matrix.branch }}. Please review the changes or revert the changes in GitHub. https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' \ + ${{ secrets.SLACK_WEBHOOK_URL }} + - name: Fail workflow + if: ${{ env.diff_failed == 'true' }} + run: | + git diff .github/branch_protection_settings/${{ matrix.branch }}.json + echo "::error::Branch protections have been changed" \ No newline at end of file diff --git a/.github/workflows/security-dependabot.yml b/.github/workflows/security-dependabot.yml new file mode 100644 index 000000000..095219045 --- /dev/null +++ b/.github/workflows/security-dependabot.yml @@ -0,0 +1,42 @@ +# Auto merges dependabot PRs +# +# Description: +# Auto-merges dependabot PRs if all checks pass +# We verify all commits in the PR to ensure no one else has committed to the PR +# +# Triggers: +# - pull_request + +on: + pull_request: + branches: [ dependabot/* ] + +name: Dependabot updates +run-name: Dependabot + +permissions: + contents: read + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'aws-powertools/powertools-lambda-java' }} + permissions: + pull-requests: read + steps: + - id: dependabot-metadata + name: Fetch Dependabot metadata + uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0 + - name: Fail workflow + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' }} + run: | + echo "::error::Major version upgrades are not wanted" + - name: Approve PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr review "${{ github.event.pull_request.html_url }}" --approve --body '🤖 Approved by another robot.' + - name: Enable auto-merge on PR + run: gh pr merge --auto --squash "${{ github.event.pull_request.html_url }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ 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..9c588d9be --- /dev/null +++ b/.github/workflows/security-dependencies-check.yml @@ -0,0 +1,39 @@ +# Dependency checks +# +# Description: +# Verifies that dependencies are compatible with our project +# by checking licenses and their security posture +# +# Triggers: +# - pull_request +# - push +# - workflow_dispatch +# - cron: daily at 12:00PM + +on: + pull_request: + workflow_dispatch: + push: + branches: [ main ] + schedule: + - cron: '0 12 * * *' # Run daily at 12:00 UTC + +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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Verify Contents + uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 + with: + config-file: './.github/dependency-review-config.yml' \ No newline at end of file diff --git a/.github/workflows/security-osv.yml b/.github/workflows/security-osv.yml new file mode 100644 index 000000000..b332faae3 --- /dev/null +++ b/.github/workflows/security-osv.yml @@ -0,0 +1,37 @@ +# Runs OSV scan +# +# Description: +# Checks dependencies already in the project for known issues +# +# Triggers: +# - pull_request +# - workflow_dispatch +# - cron +# - push + +on: + pull_request: + branches: + - main + - v2 + workflow_dispatch: {} + schedule: + - cron: "30 12 * * 1" + push: + branches: + - main + - v2 + +name: OpenSource Vulnerability Scanner +run-name: OpenSource Vulnerability Scanner + +permissions: + contents: read + +jobs: + scan-pr: + permissions: + actions: read + contents: read + security-events: write + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@764c91816374ff2d8fc2095dab36eecd42d61638 # v1.9.2 \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 82b0209f5..a959432c0 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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>1.17.0</version> + <version>2.0.0-SNAPSHOT</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/pom.xml b/pom.xml index 891420766..353eb7bee 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> <aws.sdk.version>2.28.1</aws.sdk.version> - <aws.xray.recorder.version>2.18.1</aws.xray.recorder.version> + <aws.xray.recorder.version>2.18.2</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.3</lambda.core.version> @@ -79,6 +79,15 @@ <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.13.0</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.2.5</maven-surefire-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.11.2</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> + <junit.version>5.10.0</junit.version> + <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> @@ -89,6 +98,8 @@ <junit.version>5.10.2</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.18.0</versions-maven-plugin.version> <elastic.version>1.6.0</elastic.version> <!-- As we have a .mvn directory at the root of the project, this will evaluate to the root directory @@ -335,6 +346,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> From 308c2e2536b9f86bb8dfa10b20c1b6f5bb599e52 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 7 May 2025 19:37:29 +0200 Subject: [PATCH 0653/1008] fix(ci): Update control flow to allow for better skipping of things (#1831) --- .github/workflows/release.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b650f105..f6228a1af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: 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_v1.outputs.build_matrix) }} + 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 }} @@ -161,6 +161,7 @@ jobs: - setup - quality - version_seal + if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} strategy: matrix: java: ${{ fromJson(needs.setup.outputs.build_matrix) }} @@ -183,7 +184,7 @@ jobs: publish: runs-on: ubuntu-latest - if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' }} + if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' && inputs.skip_publish == false }} needs: - build environment: Release @@ -210,7 +211,7 @@ jobs: create_pr: runs-on: ubuntu-latest - if: ${{ inputs.snapshot == false }} + if: ${{ inputs.snapshot == false && always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} needs: - build - publish From 149cf9ff66a924f818ae582d2fe41ea25c995bb8 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 7 May 2025 20:57:56 +0200 Subject: [PATCH 0654/1008] fix(ci): add user/pass to javasetup (#1832) --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6228a1af..da01b89c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -202,6 +202,9 @@ jobs: cache: maven gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} gpg-passphrase: GPG_PASSPHRASE + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD - name: Publish package run: mvn -Prelease clean deploy -DskipTests env: From 90a982034e8b2bb910780ce7914e983afb568618 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 12:00:08 +0200 Subject: [PATCH 0655/1008] build(deps): bump com.github.tomakehurst:wiremock-jre8 (#1741) Bumps [com.github.tomakehurst:wiremock-jre8](https://github.com/wiremock/wiremock) from 2.35.1 to 2.35.2. - [Release notes](https://github.com/wiremock/wiremock/releases) - [Commits](https://github.com/wiremock/wiremock/compare/2.35.1...2.35.2) --- updated-dependencies: - dependency-name: com.github.tomakehurst:wiremock-jre8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 353eb7bee..0c7f4c454 100644 --- a/pom.xml +++ b/pom.xml @@ -337,7 +337,7 @@ <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> - <version>2.35.1</version> + <version>2.35.2</version> <scope>test</scope> </dependency> </dependencies> From ad4369c52eac3596a9d43396a78cbf0fc8892964 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 12:00:29 +0200 Subject: [PATCH 0656/1008] build(deps): bump org.skyscreamer:jsonassert from 1.5.1 to 1.5.3 (#1750) Bumps [org.skyscreamer:jsonassert](https://github.com/skyscreamer/JSONassert) from 1.5.1 to 1.5.3. - [Release notes](https://github.com/skyscreamer/JSONassert/releases) - [Changelog](https://github.com/skyscreamer/JSONassert/blob/master/CHANGELOG.md) - [Commits](https://github.com/skyscreamer/JSONassert/compare/jsonassert-1.5.1...jsonassert-1.5.3) --- updated-dependencies: - dependency-name: org.skyscreamer:jsonassert dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Simon Thulbourn <sthulb@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c7f4c454..d684462ca 100644 --- a/pom.xml +++ b/pom.xml @@ -320,7 +320,7 @@ <dependency> <groupId>org.skyscreamer</groupId> <artifactId>jsonassert</artifactId> - <version>1.5.1</version> + <version>1.5.3</version> <scope>test</scope> </dependency> <dependency> From 0260bf4a8341e6e874d51b6c6cbfc7de7f47ee90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 12:03:58 +0200 Subject: [PATCH 0657/1008] build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin (#1827) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.7.0 to 3.11.2. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.7.0...maven-javadoc-plugin-3.11.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.11.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d684462ca..f0e547c9b 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> - <maven-javadoc-plugin.version>3.7.0</maven-javadoc-plugin.version> + <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> <maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version> <junit.version>5.10.2</junit.version> From fbcde4ed516a3e32cfff8c65c00d61563d1a861d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 12:04:06 +0200 Subject: [PATCH 0658/1008] build(deps): bump org.mockito:mockito-subclass from 5.6.0 to 5.17.0 (#1825) Bumps [org.mockito:mockito-subclass](https://github.com/mockito/mockito) from 5.6.0 to 5.17.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.6.0...v5.17.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-subclass dependency-version: 5.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- powertools-common/pom.xml | 4 ++-- powertools-logging/pom.xml | 4 ++-- powertools-logging/powertools-logging-log4j/pom.xml | 4 ++-- powertools-logging/powertools-logging-logback/pom.xml | 4 ++-- powertools-metrics/pom.xml | 4 ++-- powertools-serialization/pom.xml | 4 ++-- powertools-tracing/pom.xml | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 2a04688fb..ee6097310 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -84,7 +84,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -110,7 +110,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 86aeadd28..9d57c4221 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -110,7 +110,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -136,7 +136,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 5f176bb46..ac1969010 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -94,7 +94,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -120,7 +120,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 8fde683b4..4af69ab77 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -87,7 +87,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -110,7 +110,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 1211a02c1..22ac3c68e 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -106,7 +106,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -132,7 +132,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index ee2d7db3d..e2bbbe82b 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -74,7 +74,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -97,7 +97,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 59bb596db..ad0ed289f 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -107,7 +107,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -133,7 +133,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> From 2528a85a113fade67a8e712167cc83b2c0a2d64f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 11:45:46 +0200 Subject: [PATCH 0659/1008] build(deps): bump aws.sdk.version from 2.28.1 to 2.31.40 (#1840) Bumps `aws.sdk.version` from 2.28.1 to 2.31.40. Updates `software.amazon.awssdk:bom` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:http-client-spi` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:url-connection-client` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:dynamodb` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:s3` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:lambda` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:kinesis` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:cloudwatch` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:xray` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:sqs` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:cloudformation` from 2.28.1 to 2.31.40 Updates `software.amazon.awssdk:sts` from 2.28.1 to 2.31.40 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.31.40 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.31.40 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.31.40 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d2227a95b..15084d9b4 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.15.0</lambda.events.version> - <aws.sdk.version>2.28.1</aws.sdk.version> + <aws.sdk.version>2.31.40</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index f0e547c9b..9f6e49a70 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ <log4j.version>2.24.3</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.28.1</aws.sdk.version> + <aws.sdk.version>2.31.40</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 8108997412d1490806b4325d0d9107b1ecbcb9d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 11:46:13 +0200 Subject: [PATCH 0660/1008] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin (#1841) Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.4 to 3.2.7. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.4...maven-gpg-plugin-3.2.7) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-version: 3.2.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f6e49a70..e796a9e42 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> - <maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version> + <maven-gpg-plugin.version>3.2.7</maven-gpg-plugin.version> <junit.version>5.10.2</junit.version> <aws-embedded-metrics.version>4.1.2</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> From 539088a6c8c5119a0f975253fc3e1c1060a427d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 11:46:48 +0200 Subject: [PATCH 0661/1008] build(deps): bump org.apache.maven.plugins:maven-jar-plugin (#1842) Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.4.1 to 3.4.2. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.1...maven-jar-plugin-3.4.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-version: 3.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-idempotency/powertools-idempotency-dynamodb/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index c0a807ed8..3ab817209 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -79,7 +79,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>3.4.1</version> + <version>3.4.2</version> <configuration> <archive> <manifestEntries> From 52441afe3fdffeb8e894410adaca8103a2f9316a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 11:48:16 +0200 Subject: [PATCH 0662/1008] build(deps): bump org.apache.maven.plugins:maven-artifact-plugin (#1843) Bumps [org.apache.maven.plugins:maven-artifact-plugin](https://github.com/apache/maven-artifact-plugin) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-artifact-plugin/releases) - [Commits](https://github.com/apache/maven-artifact-plugin/compare/maven-artifact-plugin-3.5.0...maven-artifact-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-artifact-plugin dependency-version: 3.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e796a9e42..e06831d0c 100644 --- a/pom.xml +++ b/pom.xml @@ -409,7 +409,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-artifact-plugin</artifactId> - <version>3.5.0</version> + <version>3.6.0</version> <configuration> <reproducible>true</reproducible> </configuration> From 7ca1b7e5415353533559a93d2521522001ff929b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 13:04:35 +0200 Subject: [PATCH 0663/1008] build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin (#1844) Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.1.2 to 3.5.3. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.5.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8120e44d8..c23707600 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -210,7 +210,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <executions> <execution> <goals> From 3281f0649c2a3bcc5ee2442b685f4058a1fd754c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Tue, 13 May 2025 16:32:47 +0200 Subject: [PATCH 0664/1008] feat(v2): batch validation with partial failure (#1621) * validation of sqs / kinesis batches with partial failures * cleanup jdk8 stuff + slf4j simple logs for tests * fix sonar potential NPE * documentation * update mockito (and bytebuddy) for java21 compat * comment * fix mvn pom * Preparing for checkstyle removal. Keeping the checkstyle.xml for backwards compatibility. --------- Co-authored-by: Philipp Page <pagejep@amazon.com> Co-authored-by: Philipp Page <github@philipp.page> --- docs/utilities/validation.md | 51 ++-- pom.xml | 107 +------ powertools-batch/pom.xml | 10 + powertools-cloudformation/pom.xml | 21 +- powertools-common/pom.xml | 18 +- powertools-e2e-tests/pom.xml | 4 - .../powertools-idempotency-core/pom.xml | 10 + powertools-large-messages/pom.xml | 16 +- powertools-logging/pom.xml | 16 +- .../powertools-logging-log4j/pom.xml | 11 +- .../powertools-logging-logback/pom.xml | 11 +- powertools-metrics/pom.xml | 16 +- powertools-parameters/pom.xml | 4 - .../powertools-parameters-appconfig/pom.xml | 16 +- .../powertools-parameters-dynamodb/pom.xml | 16 +- .../powertools-parameters-secrets/pom.xml | 16 +- .../powertools-parameters-ssm/pom.xml | 16 +- .../powertools-parameters-tests/pom.xml | 10 + powertools-serialization/pom.xml | 11 +- powertools-tracing/pom.xml | 22 +- powertools-validation/pom.xml | 28 +- .../validation/internal/ValidationAspect.java | 277 ++++++++++++------ .../handlers/KinesisHandlerWithError.java | 34 +++ .../handlers/SQSHandlerWithError.java | 34 +++ .../handlers/StandardKinesisHandler.java | 32 ++ .../handlers/StandardSQSHandler.java | 32 ++ .../internal/ValidationAspectTest.java | 133 +++++---- .../resources/kinesis_invalid_messages.json | 72 +++++ .../test/resources/sqs_invalid_messages.json | 72 +++++ 29 files changed, 757 insertions(+), 359 deletions(-) create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/KinesisHandlerWithError.java create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSHandlerWithError.java create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardKinesisHandler.java create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.java create mode 100644 powertools-validation/src/test/resources/kinesis_invalid_messages.json create mode 100644 powertools-validation/src/test/resources/sqs_invalid_messages.json diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index f73f7e787..b257c5dde 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -8,7 +8,7 @@ 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 @@ -100,10 +100,15 @@ The validator is configured to enable format assertions by default even for 2019 `@Validation` annotation is used to validate either inbound events or functions' response. 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. + 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. +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. === "MyFunctionHandler.java" @@ -160,31 +165,31 @@ For the following events and responses, the Validator will automatically perform ** 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` +| 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)`` +| 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 diff --git a/pom.xml b/pom.xml index e06831d0c..0c12aa9c6 100644 --- a/pom.xml +++ b/pom.xml @@ -101,6 +101,7 @@ <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.18.0</versions-maven-plugin.version> <elastic.version>1.6.0</elastic.version> + <mockito.version>5.12.0</mockito.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. --> @@ -317,6 +318,12 @@ </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> @@ -328,6 +335,12 @@ <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>com.amazonaws</groupId> <artifactId>aws-lambda-java-tests</artifactId> @@ -603,100 +616,6 @@ </plugins> </build> </profile> - <profile> - <id>olderThanJdk11</id> - <activation> - <jdk>(,11)</jdk> - </activation> - <properties> - <!-- mockito 5+ is not compatible anymore with java < 11 --> - <mockito.version>4.11.0</mockito.version> - </properties> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>${mockito.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <version>${mockito.version}</version> - <scope>test</scope> - </dependency> - </dependencies> - </profile> - <profile> - <id>newerThanJdk11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <properties> - <mockito.version>5.6.0</mockito.version> - </properties> - <dependencies> - <!-- since mockito 5.3, no need to have mockito-inline --> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>${mockito.version}</version> - <scope>test</scope> - </dependency> - </dependencies> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <systemPropertyVariables> - <!-- TODO: remove when updating mockito / bytebuddy with Java21 compat --> - <net.bytebuddy.experimental>true</net.bytebuddy.experimental> - </systemPropertyVariables> - </configuration> - </plugin> - </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.6.0</version> - <configuration> - <propertyExpansion>basedir=${project.rootdir}</propertyExpansion> - <configLocation>checkstyle.xml</configLocation> - <consoleOutput>true</consoleOutput> - <failsOnError>true</failsOnError> - <linkXRef>false</linkXRef> - </configuration> - <!-- does not work without this dependency --> - <dependencies> - <dependency> - <groupId>com.puppycrawl.tools</groupId> - <artifactId>checkstyle</artifactId> - <version>10.18.1</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 66a5e3087..819c19927 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -73,6 +73,16 @@ <artifactId>aws-lambda-java-tests</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index e3e4748d6..36392fce2 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -75,6 +75,16 @@ <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <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> @@ -86,13 +96,4 @@ <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 +</project> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index ee6097310..47a505325 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -76,6 +76,16 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> <profile> @@ -182,11 +192,5 @@ <directory>src/main/resources</directory> </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 +</project> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index c23707600..306e8060b 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -176,10 +176,6 @@ <build> <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> <!-- Don't deploy the e2e tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 302cc24f5..59b69da0f 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -38,5 +38,15 @@ <artifactId>powertools-serialization</artifactId> </dependency> <!-- Test dependencies --> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 4206183de..afbd0aa37 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -97,6 +97,16 @@ <artifactId>junit-pioneer</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</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> @@ -130,11 +140,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-logging/pom.xml b/powertools-logging/pom.xml index 9d57c4221..62cb9c352 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -67,6 +67,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> @@ -201,10 +211,6 @@ </resource> </resources> <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> @@ -217,4 +223,4 @@ </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 index ac1969010..96a9a7043 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -51,6 +51,11 @@ <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.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> @@ -215,10 +220,6 @@ </dependency> </dependencies> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> @@ -234,4 +235,4 @@ </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 4af69ab77..a94801345 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -48,6 +48,11 @@ <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.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -208,10 +213,6 @@ </dependency> </dependencies> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> @@ -228,4 +229,4 @@ </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 22ac3c68e..d51ea5b33 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -77,6 +77,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> @@ -197,11 +207,5 @@ <directory>src/main/resources</directory> </resource> </resources> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> - </plugins> </build> </project> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 0f2b37151..09b8d3b2f 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -87,10 +87,6 @@ </environmentVariables> </configuration> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - </plugin> </plugins> </build> </project> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 34b1238f6..be47fbddb 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -52,6 +52,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -80,10 +90,6 @@ </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-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 2ec6ad27c..1713dbad5 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -53,6 +53,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -81,10 +91,6 @@ </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-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 3275d0ee0..c2ea66f5c 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -53,6 +53,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -81,10 +91,6 @@ </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-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 65332c9ef..475aca8f2 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -53,6 +53,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> @@ -81,10 +91,6 @@ </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-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index d8e9b2a02..6ab2e4155 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -48,6 +48,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e2bbbe82b..6c45b62dd 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -56,6 +56,11 @@ <artifactId>junit-jupiter-api</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> @@ -172,11 +177,7 @@ <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 +</project> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index ad0ed289f..140c212e3 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -78,6 +78,16 @@ <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.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> @@ -196,14 +206,4 @@ </build> </profile> </profiles> - - <build> - <plugins> - <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-validation/pom.xml b/powertools-validation/pom.xml index c1521f1c7..7d460eec3 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -83,7 +83,21 @@ <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.mockito</groupId> + <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> @@ -105,14 +119,4 @@ <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 +</project> 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 bcbba9e03..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 @@ -40,11 +40,15 @@ 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; @@ -57,7 +61,7 @@ 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 { @@ -77,11 +81,12 @@ public Object around(ProceedingJoinPoint pjp, if (validation.schemaVersion() != V201909) { 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 result = null; + Object validationResult = null; + boolean failFast = false; if (placedOnRequestHandler(pjp)) { validationNeeded = true; @@ -94,16 +99,19 @@ public Object around(ProceedingJoinPoint pjp, validate(obj, inboundJsonSchema, validation.envelope()); } else if (obj instanceof APIGatewayProxyRequestEvent) { APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) obj; - result = validateAPIGatewayProxyBody(event.getBody(), inboundJsonSchema, null, null); + validationResult = validateAPIGatewayProxyBody(event.getBody(), inboundJsonSchema, null, null); + failFast = true; } else if (obj instanceof APIGatewayV2HTTPEvent) { APIGatewayV2HTTPEvent event = (APIGatewayV2HTTPEvent) obj; - result = validateAPIGatewayV2HTTPBody(event.getBody(), inboundJsonSchema, null, null); + 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); @@ -118,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()); @@ -149,100 +159,183 @@ record -> validate(decode(record.getData()), inboundJsonSchema))); } } - // don't execute the lambda if result was set by previous validation step + 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 (result != null) { - LOG.error("Incoming API event's body failed inbound schema validation."); + 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."); + } + } } - else { - result = pjp.proceed(proceedArgs); - - if (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 inboundJsonSchema validation schema + * + * @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; + 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 inboundJsonSchema validation schema + * + * @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; + 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/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-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.java new file mode 100644 index 000000000..e0f0ece2d --- /dev/null +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/StandardSQSHandler.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.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import software.amazon.lambda.powertools.validation.Validation; + +public class StandardSQSHandler implements RequestHandler<SQSEvent, SQSBatchResponse> { + + @Override + @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/internal/ValidationAspectTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java index 1708ebeeb..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 @@ -20,24 +20,6 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.when; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.Signature; -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.ArgumentsSource; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; @@ -55,24 +37,45 @@ 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; +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.ArgumentsSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; 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.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.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; @@ -167,7 +170,7 @@ void testValidateOutboundJsonSchemaWithHandledExceptions(Object object) throws T } @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); @@ -187,7 +190,7 @@ public void testValidateOutboundJsonSchema_APIGWV2() throws Throwable { } @Test - public void validate_inputOK_schemaInClasspath_shouldValidate() { + void validate_inputOK_schemaInClasspath_shouldValidate() { GenericSchemaV7APIGatewayProxyRequestEventHandler handler = new GenericSchemaV7APIGatewayProxyRequestEventHandler(); APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody("{" + @@ -204,7 +207,7 @@ public void validate_inputOK_schemaInClasspath_shouldValidate() { } @Test - public void validate_inputKO_schemaInClasspath_shouldThrowValidationException() { + void validate_inputKO_schemaInClasspath_shouldThrowValidationException() { GenericSchemaV7APIGatewayProxyRequestEventHandler handler = new GenericSchemaV7APIGatewayProxyRequestEventHandler(); Map<String, String> headers = new HashMap<>(); @@ -232,7 +235,7 @@ public void validate_inputKO_schemaInClasspath_shouldThrowValidationException() } @Test - public void validate_inputOK_schemaInString_shouldValidate() { + void validate_inputOK_schemaInString_shouldValidate() { ValidationInboundAPIGatewayV2HTTPEventHandler handler = new ValidationInboundAPIGatewayV2HTTPEventHandler(); APIGatewayV2HTTPEvent event = new APIGatewayV2HTTPEvent(); event.setBody("{" + @@ -248,7 +251,7 @@ public void validate_inputOK_schemaInString_shouldValidate() { @Test - public void validate_inputKO_schemaInString_shouldThrowValidationException() { + void validate_inputKO_schemaInString_shouldThrowValidationException() { ValidationInboundAPIGatewayV2HTTPEventHandler handler = new ValidationInboundAPIGatewayV2HTTPEventHandler(); Map<String, String> headers = new HashMap<>(); @@ -268,49 +271,77 @@ public void validate_inputKO_schemaInString_shouldThrowValidationException() { assertThat(response.getMultiValueHeaders()).isEmpty(); } - @Test - public void validate_SQS() { - PojoSerializer<SQSEvent> pojoSerializer = - LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); - SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); - + @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")); - + @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); 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/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 From 9d111b396719d3076998ce0586bef960c4bf8e90 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 15 May 2025 16:23:29 +0200 Subject: [PATCH 0665/1008] fix(logging): Escape double-quotes when serializing strings into JSON. (#1845) --- .../internal/LambdaJsonEncoderTest.java | 9 +++-- .../logging/internal/JsonSerializer.java | 36 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) 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 index 9b6fb8d1c..4a7067540 100644 --- 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 @@ -127,7 +127,8 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { assertThat(contentOf(logFile)) .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \"1212abcd\"\""); + // Should auto-escape double quotes around id + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); @@ -158,7 +159,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { assertThat(contentOf(logFile)) .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \"1212abcd\"\""); + // Should auto-escape double quotes around id + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); @@ -295,7 +297,8 @@ void shouldLogEventAsStringForStreamHandler() throws IOException { File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) .contains("\"message\":\"Handler Event\"") - .contains("\"event\":\"{\"key\":\"value\"}\""); // logged as String for StreamHandler + // logged as String for StreamHandler (should auto-escape double-quotes to avoid breaking JSON format) + .contains("\"event\":\"{\\\"key\\\":\\\"value\\\"}\""); } @Test 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 index 0b4359825..82bc76a38 100644 --- 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 @@ -53,7 +53,7 @@ public JsonSerializer(StringBuilder builder) { } this.builder = builder; } - + public void writeStartArray() { builder.append('['); } @@ -84,7 +84,8 @@ public void writeString(String text) { if (text == null) { writeNull(); } else { - builder.append("\"").append(text).append("\""); + // Escape double quotes to avoid breaking JSON format + builder.append("\"").append(text.replace("\"", "\\\"")).append("\""); } } @@ -314,7 +315,7 @@ public void writeMap(final Map<?, ?> map) { writeNull(); } else { writeStartObject(); - for (Iterator<? extends Map.Entry<?, ?>> entries = map.entrySet().iterator(); entries.hasNext(); ) { + 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()) { @@ -326,16 +327,16 @@ public void writeMap(final Map<?, ?> map) { } 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; @@ -364,8 +365,8 @@ else if (value instanceof Number) { writeBoolean((Boolean) value); } else if (value instanceof AtomicBoolean) { writeBoolean(((AtomicBoolean) value).get()); - } - + } + // list & collection else if (value instanceof List) { final List<?> list = (List<?>) value; @@ -373,15 +374,14 @@ else if (value instanceof 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; @@ -411,7 +411,7 @@ else if (value instanceof char[]) { final Object[] values = (Object[]) value; writeArray(values); } - + else if (value instanceof JsonNode) { JsonNode node = (JsonNode) value; @@ -462,7 +462,7 @@ else if (value instanceof JsonNode) { case OBJECT: case POJO: writeStartObject(); - for (Iterator<Map.Entry<String, JsonNode>> entries = node.fields(); entries.hasNext(); ) { + 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()) { @@ -475,7 +475,7 @@ else if (value instanceof JsonNode) { case ARRAY: ArrayNode arrayNode = (ArrayNode) node; writeStartArray(); - for (Iterator<JsonNode> elements = arrayNode.elements(); elements.hasNext(); ) { + for (Iterator<JsonNode> elements = arrayNode.elements(); elements.hasNext();) { writeObject(elements.next()); if (elements.hasNext()) { builder.append(','); @@ -558,7 +558,7 @@ public void writeTree(TreeNode rootNode) { writeNull(); } else if (rootNode instanceof TextNode) { writeString(((TextNode) rootNode).asText()); - } else if (rootNode instanceof BooleanNode) { + } else if (rootNode instanceof BooleanNode) { writeBoolean(((BooleanNode) rootNode).asBoolean()); } else if (rootNode instanceof NumericNode) { NumericNode numericNode = (NumericNode) rootNode; @@ -595,5 +595,3 @@ public void close() { // nothing to do } } - - From 00198dddb152aceca1bc556d11356a0bcc316233 Mon Sep 17 00:00:00 2001 From: Jeroen Reijn <j.reijn@gmail.com> Date: Mon, 26 May 2025 16:42:13 +0200 Subject: [PATCH 0666/1008] feat(v2): GraalVM support for parameters module (#1824) * Initial commit of all adding graalvm metadata to all parameter modules * Introduce a working example with sam and graalvm for parameters module * Add GraalVM to example name. * Add 'mvn clean package -P native-image' to Makefile again. * Remove test dependency GraalVM metadata. * Set minimum version of core utility parameters example to Java 11 to allow GitHub workflows to check minimum compatible Java version. * remove wrongly introduced class * Set AspectJ versioon to 1.9.20.1 for core utility graalvm example. --------- Co-authored-by: Philipp Page <github@philipp.page> Co-authored-by: Philipp Page <pagejep@amazon.com> --- examples/pom.xml | 4 +- .../sam-graalvm/pom.xml | 10 +- .../sam-graalvm/Dockerfile | 14 + .../sam-graalvm/Makefile | 6 + .../sam-graalvm/README.md | 80 ++++ .../sam-graalvm/pom.xml | 204 ++++++++++ .../sam-graalvm/src/main/config/bootstrap | 4 + .../java/org/demo/parameters/MyObject.java | 0 .../demo/parameters/ParametersFunction.java | 0 .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 34 ++ .../resource-config.json | 19 + .../reflect-config.json | 25 ++ .../helloworld/native-image.properties | 1 + .../helloworld/reflect-config.json | 20 + .../helloworld/resource-config.json | 7 + .../src/main/resources/log4j2.xml | 0 .../sam-graalvm/template.yaml | 100 +++++ .../{ => sam}/README.md | 2 +- .../{ => sam}/pom.xml | 2 +- .../java/org/demo/parameters/MyObject.java | 48 +++ .../demo/parameters/ParametersFunction.java | 109 ++++++ .../sam/src/main/resources/log4j2.xml | 16 + .../{ => sam}/template.yaml | 0 powertools-parameters/pom.xml | 94 +++++ .../powertools-parameters-appconfig/pom.xml | 94 +++++ .../jni-config.json | 26 ++ .../reflect-config.json | 365 ++++++++++++++++++ .../resource-config.json | 59 +++ .../powertools-parameters-dynamodb/pom.xml | 94 +++++ .../jni-config.json | 22 ++ .../reflect-config.json | 326 ++++++++++++++++ .../resource-config.json | 23 ++ .../powertools-parameters-secrets/pom.xml | 94 +++++ .../jni-config.json | 22 ++ .../reflect-config.json | 323 ++++++++++++++++ .../resource-config.json | 23 ++ .../powertools-parameters-ssm/pom.xml | 96 ++++- .../powertools-parameters-ssm/jni-config.json | 26 ++ .../reflect-config.json | 341 ++++++++++++++++ .../resource-config.json | 23 ++ .../powertools-parameters-tests/pom.xml | 94 +++++ .../powertools-parameters/reflect-config.json | 197 ++++++++++ 46 files changed, 3098 insertions(+), 9 deletions(-) create mode 100644 examples/powertools-examples-parameters/sam-graalvm/Dockerfile create mode 100644 examples/powertools-examples-parameters/sam-graalvm/Makefile create mode 100644 examples/powertools-examples-parameters/sam-graalvm/README.md create mode 100644 examples/powertools-examples-parameters/sam-graalvm/pom.xml create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/config/bootstrap rename examples/powertools-examples-parameters/{ => sam-graalvm}/src/main/java/org/demo/parameters/MyObject.java (100%) rename examples/powertools-examples-parameters/{ => sam-graalvm}/src/main/java/org/demo/parameters/ParametersFunction.java (100%) create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json create mode 100644 examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json rename examples/powertools-examples-parameters/{ => sam-graalvm}/src/main/resources/log4j2.xml (100%) create mode 100644 examples/powertools-examples-parameters/sam-graalvm/template.yaml rename examples/powertools-examples-parameters/{ => sam}/README.md (96%) rename examples/powertools-examples-parameters/{ => sam}/pom.xml (98%) create mode 100644 examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/MyObject.java create mode 100644 examples/powertools-examples-parameters/sam/src/main/java/org/demo/parameters/ParametersFunction.java create mode 100644 examples/powertools-examples-parameters/sam/src/main/resources/log4j2.xml rename examples/powertools-examples-parameters/{ => sam}/template.yaml (100%) create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/jni-config.json create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/reflect-config.json create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig/resource-config.json create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/jni-config.json create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/reflect-config.json create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb/resource-config.json create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/jni-config.json create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/reflect-config.json create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets/resource-config.json create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/jni-config.json create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/reflect-config.json create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm/resource-config.json create mode 100644 powertools-parameters/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters/reflect-config.json diff --git a/examples/pom.xml b/examples/pom.xml index 1f985a9cf..63625214c 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -30,12 +30,14 @@ <modules> <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</module> - <module>powertools-examples-parameters</module> + <module>powertools-examples-parameters/sam</module> + <module>powertools-examples-parameters/sam-graalvm</module> <module>powertools-examples-serialization</module> <module>powertools-examples-batch</module> <module>powertools-examples-validation</module> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 4ba4db943..d25e1fcb7 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -2,17 +2,17 @@ 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> + <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> - <artifactId>powertools-examples-core-utilitiessam-graalvm</artifactId> + <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> <properties> <log4j.version>2.24.0</log4j.version> - <maven.compiler.source>21</maven.compiler.source> - <maven.compiler.target>21</maven.compiler.target> - <aspectj.version>1.9.22.1</aspectj.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> diff --git a/examples/powertools-examples-parameters/sam-graalvm/Dockerfile b/examples/powertools-examples-parameters/sam-graalvm/Dockerfile new file mode 100644 index 000000000..a690606ad --- /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:latest + +#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..34f469c6c --- /dev/null +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -0,0 +1,204 @@ +<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.0.0-SNAPSHOT</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.24.0</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.2.3</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.15.0</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.1.1</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.1.1</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.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.2.5</version> + </plugin> + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</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.0</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.1.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.2</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.10.1</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 100% 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 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..d30696750 --- /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.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"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..106edef38 --- /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,34 @@ +[ + { + "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.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"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/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..7cc78a494 --- /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": "\\Qaarch64/aws-lambda-runtime-interface-client.glibc.so\\E" + }, + { + "pattern": "\\Qaarch64/aws-lambda-runtime-interface-client.musl.so\\E" + }, + { + "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.glibc.so\\E" + }, + { + "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.musl.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-parameters/src/main/resources/log4j2.xml b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-parameters/src/main/resources/log4j2.xml rename to examples/powertools-examples-parameters/sam-graalvm/src/main/resources/log4j2.xml 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/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml similarity index 98% rename from examples/powertools-examples-parameters/pom.xml rename to examples/powertools-examples-parameters/sam/pom.xml index 5737ffdeb..d0d8028d6 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <version>2.0.0-SNAPSHOT</version> - <artifactId>powertools-examples-parameters</artifactId> + <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> 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/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 09b8d3b2f..44292c755 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -89,4 +89,98 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</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 index be47fbddb..f4af26062 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -79,6 +79,100 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <plugins> <plugin> 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..7e7b40197 --- /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,365 @@ +[ +{ + "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"] }] +} +] 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-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 1713dbad5..fde6c190b 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -80,6 +80,100 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <plugins> <plugin> 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..77e953aac --- /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,326 @@ +[ +{ + "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"] }] +} +] 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-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index c2ea66f5c..a652f14e0 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -80,6 +80,100 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <plugins> <plugin> 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..7dfaf1b03 --- /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,323 @@ +[ +{ + "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"] }] +} +] 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-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 475aca8f2..ec13c10cc 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -93,4 +93,98 @@ </plugin> </plugins> </build> -</project> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> \ No newline at end of file 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..5bc2deb4a --- /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,341 @@ +[ +{ + "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"] }] +} +] 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-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 6ab2e4155..e62323bd6 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -74,4 +74,98 @@ <scope>test</scope> </dependency> </dependencies> + + <profiles> + <profile> + <id>generate-graalvm-files</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.2.3</version> + <configuration> + <argLine>-Dmockito.mock.maker=subclass -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> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.2</version> <!-- or newer 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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> + <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> + <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> + <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> + <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> + <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + </buildArg> + <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:Log=registerResource:5</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> \ No newline at end of file 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"] }] +} +] From 8066e0385a439c49bbc57b88eb38b3ce5747860f Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 27 May 2025 10:11:59 +0200 Subject: [PATCH 0667/1008] docs(v2): Create upgrade guide and versioning policy (#1856) * Update docs requirements using pip-compile. Add privacy plugin. Add llms.txt plugin. Make Idempotency documentation environment variable naming consistent. Fix documentation formatting issue in FAQ.md. * Add roadmap. * Fix outdated AspectJ configuration in documentation pages. * Synchronize Changelog with recent releases. * Add llms.txt to links to navbar. * Add Versioning policy page. * Update site url in mkdocs.yml. * Add versioning.md to llms.txt. * Update maintainers.md. * Fix formatting. * Fix formatting. * Add upgrade guide. * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/upgrade.md Co-authored-by: Leandro Damascena <lcdama@amazon.pt> * Update docs/processes/maintainers.md * Update docs/upgrade.md * Update docs/processes/maintainers.md * Update docs/upgrade.md * Update docs/upgrade.md * Fix formatting. Make Maintainer playbook naming consistent. --------- Co-authored-by: Andrea Amorosi <dreamorosi@gmail.com> Co-authored-by: Stefano Vozza <svozza@gmail.com> Co-authored-by: Leandro Damascena <lcdama@amazon.pt> --- docs/processes/maintainers.md | 22 +- docs/processes/versioning.md | 60 +++++ docs/upgrade.md | 410 +++++++++++++++++++++++++++++ docs/utilities/custom_resources.md | 9 +- docs/utilities/validation.md | 6 +- mkdocs.yml | 5 +- 6 files changed, 494 insertions(+), 18 deletions(-) create mode 100644 docs/processes/versioning.md create mode 100644 docs/upgrade.md diff --git a/docs/processes/maintainers.md b/docs/processes/maintainers.md index 6b3d9e126..8f7f6a8fd 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 | +| Simon Thulbourn | [sthulb](https://github.com/sthulb){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 | +| --------------------- | -------------------------------------------------------------------------------------- | ------------- | +| 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..8b12e0fa9 --- /dev/null +++ b/docs/processes/versioning.md @@ -0,0 +1,60 @@ +--- +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) | 1.x | General Availability | 11/04/2020 | See [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v1.0.0) | diff --git a/docs/upgrade.md b/docs/upgrade.md new file mode 100644 index 000000000..11be3dc5f --- /dev/null +++ b/docs/upgrade.md @@ -0,0 +1,410 @@ +--- +title: Upgrade guide +description: Guide to update between major Powertools for AWS Lambda (Java) versions +--- + +## End of support v1 + +<!-- TODO: Add end of support banner here once developer preview started. --> + +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 Powertools Logging 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 --> + +The Metrics utility is currently undergoing changes to the public interface as part of GitHub issue [#1848](https://github.com/aws-powertools/powertools-lambda-java/issues/1848). We will keep this upgrade guide updated with the most recent changes as soon as they are released. Stay tuned for updates! + +## 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/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/validation.md b/docs/utilities/validation.md index b257c5dde..96bdd142b 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -163,7 +163,7 @@ 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 ** +**Events** | Type of event | Class | Path to content | |---------------------------------|-------------------------------------------------|----------------------------------------------| @@ -181,7 +181,7 @@ For the following events and responses, the Validator will automatically perform | SNS | SNSEvent | `Records[*].Sns.Message` | | SQS | SQSEvent | `Records[*].body` | -** Responses ** +**Responses** | Type of response | Class | Path to content (envelope) | |-----------------------|---------------------------------------------|---------------------------------------| @@ -189,7 +189,7 @@ For the following events and responses, the Validator will automatically perform | API Gateway HTTP | APIGatewayV2HTTPResponse} | `body` | | API Gateway WebSocket | APIGatewayV2WebSocketResponse} | `body` | | Load Balancer | ApplicationLoadBalancerResponseEvent} | `body` | -| Kinesis Analytics | KinesisAnalyticsInputPreprocessingResponse} | `Records[*].powertools_base64(data)`` | +| Kinesis Analytics | KinesisAnalyticsInputPreprocessingResponse} | `Records[*].powertools_base64(data)` | ## Custom events and responses diff --git a/mkdocs.yml b/mkdocs.yml index cf73d41fc..ee5f77dde 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,10 +1,11 @@ site_name: Powertools for AWS Lambda (Java) Preview site_description: Powertools for AWS Lambda (Java) Preview site_author: Amazon Web Services -site_url: https://docs.powertools.aws.dev/lambda-java/ +site_url: https://docs.powertools.aws.dev/lambda/java/preview/ nav: - Homepage: index.md - Changelog: changelog.md + - Upgrade Guide: upgrade.md - FAQs: FAQs.md - Roadmap: roadmap.md - Core utilities: @@ -21,6 +22,7 @@ nav: - utilities/serialization.md - Processes: - processes/maintainers.md + - "Versioning policy": processes/versioning.md - Resources: - "llms.txt": ./llms.txt - "llms.txt (full version)": ./llms-full.txt @@ -106,6 +108,7 @@ plugins: - utilities/serialization.md Processes: - processes/maintainers.md + - processes/versioning.md extra_css: - stylesheets/extra.css From 875e9936bab8adbb48ca5c0b32354c762a8065f7 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 28 May 2025 10:45:49 +0200 Subject: [PATCH 0668/1008] chore(ci): Publish to Maven Central instead of OSSRH instance (#1858) * Add Maven Central as release target. * Update secret location for Maven Central credentials. --- .github/workflows/release.yml | 8 ++++---- pom.xml | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da01b89c1..826536136 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -202,14 +202,14 @@ jobs: cache: maven gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} gpg-passphrase: GPG_PASSPHRASE - server-id: ossrh + server-id: central server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD - name: Publish package run: mvn -Prelease clean deploy -DskipTests env: - MAVEN_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} + MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} create_pr: @@ -290,4 +290,4 @@ jobs: run: | aws s3 sync \ dist \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/ \ No newline at end of file + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/ diff --git a/pom.xml b/pom.xml index 0c12aa9c6..d78daeb4e 100644 --- a/pom.xml +++ b/pom.xml @@ -110,8 +110,8 @@ <distributionManagement> <snapshotRepository> - <id>ossrh</id> - <url>https://aws.oss.sonatype.org/content/repositories/snapshots</url> + <id>central-portal-snapshots</id> + <url>https://central.sonatype.com/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement> @@ -478,13 +478,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.7.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> From 54ef1e9daa925e707d140221981ec84bf3493260 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 28 May 2025 10:46:44 +0200 Subject: [PATCH 0669/1008] fix(ci): Fix failing E2E tests and temporarily exclude TracingE2E (#1847) * fix(ci): Remove unused workflows and add JAVA_VERSION env var to e2e tests. * Test e2e test for Java 11 only. * Debug Parameteres E2E test only. * Revert "Debug Parameteres E2E test only." This reverts commit 06648527c86be6e2616173d396db751dbe8e51f9. * Exlucde TracingE2ET for testing. * Enable Java version matrix again. --- .github/workflows/auto-merge.yml | 66 ----------------------- .github/workflows/check-e2e.yml | 5 +- .github/workflows/docs-v2-snapshot.yml | 40 -------------- .github/workflows/publish-v2-snapshot.yml | 29 ---------- powertools-e2e-tests/pom.xml | 10 ++-- 5 files changed, 11 insertions(+), 139 deletions(-) delete mode 100644 .github/workflows/auto-merge.yml delete mode 100644 .github/workflows/docs-v2-snapshot.yml delete mode 100644 .github/workflows/publish-v2-snapshot.yml diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml deleted file mode 100644 index 5401eedc9..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@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - uses: ahmadnassri/action-workflow-run-wait@2aa3d9e1a12ecaaa9908e368eaf2123bb084323e # v1.4.4 - with: - timeout: 300000 - - name: 'Download artifact' - uses: actions/github-script@47f7cf65b5ced0830a325f705cad64f2f58dddf7 # 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@47f7cf65b5ced0830a325f705cad64f2f58dddf7 # v3.1.0 - 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/check-e2e.yml b/.github/workflows/check-e2e.yml index 6420cd6f7..14eab5394 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -54,6 +54,7 @@ jobs: - 11 - 17 - 21 + steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java @@ -68,4 +69,6 @@ jobs: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 - 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 + env: + JAVA_VERSION: ${{ matrix.java }} + run: mvn -DskipTests install --file pom.xml && mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml diff --git a/.github/workflows/docs-v2-snapshot.yml b/.github/workflows/docs-v2-snapshot.yml deleted file mode 100644 index 55803c737..000000000 --- a/.github/workflows/docs-v2-snapshot.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Docs -on: - push: - branches: - - v2 - workflow_dispatch: {} - -permissions: - id-token: write - contents: write - pages: write - -jobs: - docs: - runs-on: ubuntu-latest - environment: Docs - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Set up Python - uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1 - 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/preview/ diff --git a/.github/workflows/publish-v2-snapshot.yml b/.github/workflows/publish-v2-snapshot.yml deleted file mode 100644 index d5a683261..000000000 --- a/.github/workflows/publish-v2-snapshot.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Publish v2 -on: - push: - branches: - - v2 - workflow_dispatch: {} -jobs: - publish: - runs-on: ubuntu-latest - environment: snapshot - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Set up Maven Central Repository - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: 11 - server-id: ossrh - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - # TODO: use environments https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment - 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: Publish package - run: mvn -Prelease clean validate deploy -DskipTests # We use validate here to run maven enforcer, to make sure we are only publishing SNAPSHOT builds - env: - MAVEN_USERNAME: ${{ secrets.SNAPSHOT_PUBLISH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.SNAPSHOT_PUBLISH_PASSWORD }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 306e8060b..9cb604f56 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/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> <parent> <artifactId>powertools-parent</artifactId> @@ -216,10 +216,14 @@ </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> + <excludes> + <exclude>**/TracingE2ET.java</exclude> + </excludes> </configuration> </plugin> </plugins> From 4444b4bce8eb1cc19880d1c1ef07188d97de9126 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 28 May 2025 12:33:43 +0200 Subject: [PATCH 0670/1008] chore(ci): Set snapshot repository to "central" server ID --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d78daeb4e..cb761183e 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ <distributionManagement> <snapshotRepository> - <id>central-portal-snapshots</id> + <id>central</id> <url>https://central.sonatype.com/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement> From 57ae1534ac4d837b24deac8027e577db7a7ad6af Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 6 Jun 2025 11:56:44 +0200 Subject: [PATCH 0671/1008] feat(metrics): New metrics module implementation with support for Metrics providers and usage without annotations (#1863) * Rewrite metrics module with interface abstraction and provider pattern to be able to support multiple metrics providers. * Update default dimension and namespace logic to have the precedence: @Metrics annotation. MetricsLoggerBuilder, Environment variables. * Small cosmetic changes. * Add unit tests. * Update metrics documentation with new interface. * Make unit tests compatible with GraalVM. It is not supported to mock interface with Mockito. * Add GraalVM support bullet points to docs pages. * Update GraalVM metadata files after re-implementing metrics module. * Fix spotbugs issue. This is expected for this factory class for MetricsLogger to enable user configuration of the MetricsLogger singleton. * Fix SonarCube findings. * Rename back namespace to ServerlessAirline example. * Fix DimensionSet validation to be inline with most recent specification. * Overload void addDimension(DimensionSet dimensionSet) to be able to add multi-dimensional dimensions also for non default dimensions. * Update setDefaultDimensions to take a DimensionSet instead of a Map. * Add warning if not emitting metrics and raiseOnEmptyMetrics is false. * Remove unused imports. * Add namespace validation. Refacator into own Validator class. Require namespace to be set or raise an exception otherwise. * Update docs with DimensionSet.of instead of Map.of. Fix naming consistency. * Add example for overwriting dimension of cold start metric. * Add note that metadata needs to be added before flushing. * Remove test classes from GRM reflect-config.json. * Rename pushSingleMetric to flushSingleMetric. * Add example for high cardinality dimensions using DimensionSet. * Add support for POWERTOOLS_METRICS_DISABLED environment variable. * Only add service name dimension if the service is actually defined. * Add support for POWERTOOLS_METRICS_FUNCTION_NAME. * Add Testing your code section to docs. * Rename @Metrics to @FlushMetrics. * Rename MetricsLogger to Metrics, rename MetricsLoggerBuilder to MetricsBuilder, rename MetricsLoggerFactory to MetricsFactory. * Add support for setTimestamp. * Update regex for namespace validation. * Fix namespace regex and fix metrics e2e tests. * Add comment in empty callAt method to satisfy pmd_analyse. * Replace usages of standalone Powertools wording with alternative wording. * Reduce cognitive complexity of around() method in LambdaMetricsAspect. * Re-generate GRM files after applying MetricsLogger renaming. --- docs/FAQs.md | 43 +- docs/core/logging.md | 11 +- docs/core/metrics.md | 474 +++++++++++---- docs/core/tracing.md | 3 +- docs/index.md | 5 +- docs/upgrade.md | 2 +- docs/utilities/parameters.md | 1 + .../cdk/app/src/main/java/helloworld/App.java | 26 +- .../src/main/java/helloworld/AppStream.java | 4 +- .../gradle/src/main/java/helloworld/App.java | 23 +- .../src/main/java/helloworld/AppStream.java | 4 +- .../kotlin/src/main/kotlin/helloworld/App.kt | 29 +- .../src/main/kotlin/helloworld/AppStream.kt | 2 +- .../src/main/java/helloworld/App.java | 29 +- .../sam/src/main/java/helloworld/App.java | 40 +- .../src/main/java/helloworld/AppStream.java | 4 +- .../src/main/java/helloworld/App.java | 25 +- .../src/main/java/helloworld/AppStream.java | 4 +- .../src/main/java/helloworld/App.java | 25 +- .../src/main/java/helloworld/AppStream.java | 4 +- pom.xml | 29 +- .../lambda/powertools/e2e/Function.java | 31 +- powertools-metrics/pom.xml | 67 ++- .../emf/model/MetricsLoggerHelper.java | 42 -- .../powertools/metrics/FlushMetrics.java | 71 +++ .../lambda/powertools/metrics/Metrics.java | 209 +++++-- .../powertools/metrics/MetricsBuilder.java | 144 +++++ .../powertools/metrics/MetricsFactory.java | 71 +++ .../powertools/metrics/MetricsUtils.java | 172 ------ .../metrics/internal/EmfMetricsLogger.java | 326 ++++++++++ .../metrics/internal/LambdaMetricsAspect.java | 153 +++-- .../metrics/internal/Validator.java | 135 +++++ .../metrics/model/DimensionSet.java | 193 ++++++ .../MetricResolution.java} | 18 +- .../powertools/metrics/model/MetricUnit.java | 58 ++ .../metrics/provider/EmfMetricsProvider.java | 31 + .../metrics/provider/MetricsProvider.java | 30 + .../powertools-metrics/jni-config.json | 51 +- .../powertools-metrics/reflect-config.json | 433 +++++--------- .../powertools-metrics/resource-config.json | 28 +- .../metrics/ConfigurationPrecedenceTest.java | 206 +++++++ .../metrics/MetricsBuilderTest.java | 186 ++++++ .../metrics/MetricsFactoryTest.java | 166 ++++++ .../powertools/metrics/MetricsLoggerTest.java | 232 -------- ...ertoolsMetricsColdStartEnabledHandler.java | 35 -- ...MetricsEnabledDefaultDimensionHandler.java | 46 -- ...tricsEnabledDefaultNoDimensionHandler.java | 45 -- .../PowertoolsMetricsEnabledHandler.java | 41 -- ...PowertoolsMetricsEnabledStreamHandler.java | 35 -- ...sMetricsExceptionWhenNoMetricsHandler.java | 34 -- .../PowertoolsMetricsNoDimensionsHandler.java | 36 -- ...etricsNoExceptionWhenNoMetricsHandler.java | 34 -- ...rtoolsMetricsTooManyDimensionsHandler.java | 40 -- ...wertoolsMetricsWithExceptionInHandler.java | 33 - .../internal/EmfMetricsLoggerTest.java | 521 ++++++++++++++++ .../internal/LambdaMetricsAspectTest.java | 562 ++++++++---------- .../metrics/internal/ValidatorTest.java | 224 +++++++ .../metrics/model/DimensionSetTest.java | 166 ++++++ .../provider/EmfMetricsProviderTest.java | 39 ++ .../metrics/testutils/TestContext.java | 77 +++ .../metrics/testutils/TestMetrics.java | 85 +++ .../testutils/TestMetricsProvider.java | 11 + .../test/resources/simplelogger.properties | 7 + spotbugs-exclude.xml | 4 +- 64 files changed, 4044 insertions(+), 1871 deletions(-) delete mode 100644 powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/FlushMetrics.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsBuilder.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java delete mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/Validator.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/DimensionSet.java rename powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/{ValidationException.java => model/MetricResolution.java} (66%) create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricUnit.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProvider.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/provider/MetricsProvider.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java delete mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/ValidatorTest.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/model/DimensionSetTest.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/provider/EmfMetricsProviderTest.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetrics.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetricsProvider.java create mode 100644 powertools-metrics/src/test/resources/simplelogger.properties diff --git a/docs/FAQs.md b/docs/FAQs.md index f3fd2514d..75f699c91 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -3,16 +3,15 @@ title: FAQs description: Frequently Asked Questions --- - ## How can I use Powertools for AWS Lambda (Java) with Lombok? -Powertools 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. 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 +28,14 @@ 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? -Powertools 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. 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> @@ -49,17 +48,17 @@ To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` con ## How can I use Powertools for AWS Lambda (Java) with the AWS CRT HTTP Client? -Powertools uses 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. +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. +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. +Using the `aws-crt-client` in your project requires the exclusion of the `url-connection-client` transitive dependency from the `powertools-*` dependency. -```xml +```xml <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parameters</artifactId> @@ -72,8 +71,9 @@ Using the `aws-crt-client` in your project requires the exclusion of the `url-co </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. + +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. @@ -102,10 +102,11 @@ By specifying the specific target runtime, we prevent other target runtimes from ``` ### 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 Powertools module, there is a different way to configure the SDK client. -The following example shows how to use the Lambda Powertools Parameters module while leveraging the AWS CRT 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; @@ -141,12 +142,12 @@ public class RequestHandlerWithParams implements RequestHandler<String, String> } ``` -The `aws-crt-client` was considered for adoption as the default HTTP client in Lambda Powertools for Java as mentioned in [Move SDK http client to CRT](https://github.com/aws-powertools/powertools-lambda-java/issues/1092), +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? -Powertools 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. +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: @@ -157,6 +158,7 @@ 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 @@ -245,10 +247,11 @@ Create a Docker image using a `Dockerfile` like [this](../examples/powertools-ex 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. +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 1160f62ff..2d9e57dda 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -13,6 +13,7 @@ Logging provides an opinionated logger with output structured as JSON. * Optionally logs Lambda response * Optionally supports log sampling by including a configurable percentage of DEBUG logs in logging output * Allows additional keys to be appended to the structured log at any point in time +* GraalVM support ## Getting started @@ -219,7 +220,7 @@ 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/tree/v2/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json){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"?> @@ -277,7 +278,7 @@ If the level is set to any other value, we set it to the default value (`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. -When enabled, you should keep Powertools and ALC log level in sync to avoid data loss. +When enabled, you should keep your own log level and ALC log level in sync to avoid data loss. 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 --> @@ -308,7 +309,7 @@ We prioritise log level settings in this order: 2. `POWERTOOLS_LOG_LEVEL` environment variable 3. level defined in the `log4j2.xml` or `logback.xml` files -If you set Powertools level lower than ALC, we will emit a warning informing you that your messages will be discarded by Lambda. +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** > @@ -738,7 +739,7 @@ When debugging in non-production environments, you can instruct the `@Logging` a ``` ???+ note - If you use this on a RequestStreamHandler, Powertools must duplicate input streams in order to log them. + If you use this on a RequestStreamHandler, the SDK must duplicate input streams in order to log them. ## Logging handler response @@ -973,7 +974,7 @@ You can also customize how [exceptions are logged](https://logging.apache.org/lo 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 Powertools [`LambdaJsonEncoder`](): +Logback configuration is done in _logback.xml_ and the `LambdaJsonEncoder`: ```xml <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 6083d935a..35d08b140 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" /> @@ -88,30 +94,44 @@ If you're new to Amazon CloudWatch, there are two terminologies you must be awar id 'java' id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' } - + repositories { mavenCentral() } - + dependencies { aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' } - + sourceCompatibility = 11 targetCompatibility = 11 ``` ## Getting started -Metric has two global settings that will be used across all metrics emitted: +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 | 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` | +| 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-metrics-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. + === "template.yaml" ```yaml hl_lines="9 10" @@ -129,101 +149,159 @@ Metric has two global settings that will be used across all metrics emitted: === "MetricsEnabledHandler.java" - ```java hl_lines="8" - import software.amazon.lambda.powertools.metrics.Metrics; - + ```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> { - - 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) { // ... } } ``` -You can initialize Metrics anywhere in your code as many times as you need - It'll keep track of your aggregate metrics in memory. +`Metrics` is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. To guarantee that metrics are flushed properly the `@FlushMetrics` annotation must be added on the lambda handler. + +!!!info "You can use the Metrics utility without the `@FlushMetrics` annotation and flush manually. Read more in the [advanced section below](#usage-without-metrics-annotation)." ## Creating metrics -You can create metrics using `putMetric`, and manually create dimensions for all your aggregate metrics using `putDimensions`. +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="11 12" + ```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; + 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) { - metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); - metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); + metrics.addDimension("environment", "prod"); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); // ... } } ``` -!!! tip "The `Unit` enum facilitate finding a supported metric unit by CloudWatch." - -!!! 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. +!!! tip "The `MetricUnit` enum facilitates finding a supported metric unit by CloudWatch." +<!-- 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 --> ### Adding high-resolution metrics You can create [high-resolution metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics) -passing a `storageResolution` to the `putMetric` method: +passing a `#!java MetricResolution.HIGH` to the `addMetric` method. If nothing is passed `#!java MetricResolution.STANDARD` will be used. === "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.cloudwatchlogs.emf.model.StorageResolution; + 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.putMetric("SuccessfulBooking", 1, Unit.COUNT, StorageResolution.HIGH); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT, MetricResolution.HIGH); } } ``` +<!-- 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> { + + 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) { ... } @@ -232,17 +310,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) { ... } @@ -251,33 +329,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 `putMetadata` for advanced use cases, where you want to metadata as part of the serialized metrics object. +You can use `addMetadata` for advanced use cases, where you want to add 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.** + This will not be available during metrics visualization, use Dimensions for this purpose. + +!!! info + 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 ... } } @@ -285,79 +407,225 @@ 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 a single metric 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 a single metric with its own namespace and dimensions using `flushSingleMetric`: === "App.java" - ```java hl_lines="7 8 9" - import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + ```java hl_lines="12-18" + 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.flushSingleMetric( + "CustomMetric", + 1, + MetricUnit.COUNT, + "CustomNamespace", + DimensionSet.of("CustomDimension", "value") // Dimensions are optional + ); } } ``` -## 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 + +The `Metrics` Singleton provides all configuration options via `MetricsBuilder` in addition to the `@FlushMetrics` annotation. This can be useful if work in an environment or 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. Note that it is necessary to manually flush metrics now. === "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 23" + 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 without annotation + private static final Metrics customMetrics = 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. + customMetrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); + + // Add metrics to the custom metrics singleton + customMetrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + customMetrics.flush(); } } ``` + +## Testing your code + +### Suppressing metrics output + +If you would like to suppress metrics output during your unit tests, you can use the `POWERTOOLS_DISABLE_METRICS` 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_DISABLE_METRICS>true</POWERTOOLS_DISABLE_METRICS> + </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. + +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="23 28 33 50-55" +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 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; +import software.amazon.lambda.powertools.metrics.testutils.TestContext; + +class MetricsTestExample { + + private final PrintStream standardOut = 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() { + System.setOut(standardOut); + } + + @Test + void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Context context = new TestContext(); + 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"); + } + + 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"; + } + } +} +``` diff --git a/docs/core/tracing.md b/docs/core/tracing.md index ea3174fba..883f8db86 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,6 +14,7 @@ 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 diff --git a/docs/index.md b/docs/index.md index ef30b4197..43d1ea03b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -236,12 +236,13 @@ Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj **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/upgrade.md b/docs/upgrade.md index 11be3dc5f..d2b1af096 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -112,7 +112,7 @@ This step is only required if you are using log4j2 as your logging implementatio **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 Powertools Logging without large code refactorings. This improvement requires the migration of non-native SLF4J primitives from the v1 Logging utility. +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." diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index e5fb11800..beb460aa6 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -14,6 +14,7 @@ or [AWS AppConfig](https://aws.amazon.com/systems-manager/features/appconfig/). * 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 In order to provide lightweight dependencies, each parameters module is available as its own diff --git a/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/cdk/app/src/main/java/helloworld/App.java index 5aa268ffe..bb21f84d3 100644 --- a/examples/powertools-examples-core-utilities/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; @@ -32,10 +30,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; 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; @@ -45,22 +45,23 @@ */ 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) - @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); MDC.put("test", "willBeLogged"); @@ -72,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 index 94806cc38..8bc57b201 100644 --- 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 @@ -26,7 +26,7 @@ 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.Metrics; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import java.io.InputStreamReader; import java.io.BufferedReader; @@ -40,7 +40,7 @@ public class AppStream implements RequestStreamHandler { @Override @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = 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 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 index b1a701b8f..39ed6f8d8 100644 --- 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 @@ -15,8 +15,6 @@ package helloworld; import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -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; @@ -33,10 +31,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; 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; @@ -46,23 +46,24 @@ */ 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) - @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); MDC.put("test", "willBeLogged"); 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 index 94806cc38..8bc57b201 100644 --- 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 @@ -26,7 +26,7 @@ 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.Metrics; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import java.io.InputStreamReader; import java.io.BufferedReader; @@ -40,7 +40,7 @@ public class AppStream implements RequestStreamHandler { @Override @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = 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 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 index 8e8857079..b05e0d055 100644 --- 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 @@ -20,13 +20,13 @@ 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.cloudwatchlogs.emf.logger.MetricsLogger -import software.amazon.cloudwatchlogs.emf.model.DimensionSet -import software.amazon.cloudwatchlogs.emf.model.Unit 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.MetricsUtils +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 @@ -39,16 +39,23 @@ import java.net.URL */ 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) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + @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") - MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) - MetricsUtils.withSingleMetric("CustomMetrics2", 1.0, Unit.COUNT, "Another") { metric: MetricsLogger -> - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")) - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")) - } + + 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 { @@ -89,6 +96,4 @@ class App : RequestHandler<APIGatewayProxyRequestEvent?, APIGatewayProxyResponse reader.readText().trim() } } - - private val log = LoggerFactory.getLogger(this::class.java) } 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 index 99f6bbfa0..88f578e3f 100644 --- 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 @@ -24,7 +24,7 @@ import java.io.OutputStream class AppStream : RequestStreamHandler { @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = 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) 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 index e7c410042..68664feec 100644 --- 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 @@ -15,8 +15,6 @@ package helloworld; import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -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; @@ -33,11 +31,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.StorageResolution; -import software.amazon.cloudwatchlogs.emf.model.Unit; 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; @@ -47,25 +47,25 @@ */ 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) - @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); - metricsLogger().putMetric("CustomMetric3", 1, Unit.COUNT, StorageResolution.HIGH); + metrics.addMetric("CustomMetric3", 1, MetricUnit.COUNT, MetricResolution.HIGH); MDC.put("test", "willBeLogged"); @@ -77,8 +77,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/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java index e7c410042..2675c96eb 100644 --- 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 @@ -15,14 +15,8 @@ package helloworld; import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -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; -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; @@ -30,14 +24,23 @@ 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.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.StorageResolution; -import software.amazon.cloudwatchlogs.emf.model.Unit; + +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; @@ -47,25 +50,25 @@ */ 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) - @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 = new DimensionSet(); + dimensionSet.addDimension("AnotherService", "CustomService"); + dimensionSet.addDimension("AnotherService1", "CustomService1"); + metrics.flushSingleMetric("CustomMetric2", 1, MetricUnit.COUNT, "Another", dimensionSet); - metricsLogger().putMetric("CustomMetric3", 1, Unit.COUNT, StorageResolution.HIGH); + metrics.addMetric("CustomMetric3", 1, MetricUnit.COUNT, MetricResolution.HIGH); MDC.put("test", "willBeLogged"); @@ -77,8 +80,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/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java index 94806cc38..8bc57b201 100644 --- 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 @@ -26,7 +26,7 @@ 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.Metrics; +import software.amazon.lambda.powertools.metrics.FlushMetrics; import java.io.InputStreamReader; import java.io.BufferedReader; @@ -40,7 +40,7 @@ public class AppStream implements RequestStreamHandler { @Override @Logging(logEvent = true) - @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = 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 diff --git a/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java index e0b1a2979..771f5c1f1 100644 --- a/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/App.java @@ -15,8 +15,6 @@ package helloworld; import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -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; @@ -33,10 +31,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.slf4j.MDC; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; 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; @@ -46,23 +46,23 @@ */ 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) - @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); MDC.put("test", "willBeLogged"); @@ -74,8 +74,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/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/serverless/src/main/java/helloworld/AppStream.java index 401ef8c48..c13ab9f2e 100644 --- a/examples/powertools-examples-core-utilities/serverless/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-core-utilities/terraform/src/main/java/helloworld/App.java b/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/App.java index e0b1a2979..771f5c1f1 100644 --- 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 @@ -15,8 +15,6 @@ package helloworld; import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -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; @@ -33,10 +31,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.slf4j.MDC; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.Unit; 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; @@ -46,23 +46,23 @@ */ 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) - @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); MDC.put("test", "willBeLogged"); @@ -74,8 +74,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/terraform/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/terraform/src/main/java/helloworld/AppStream.java index 401ef8c48..c13ab9f2e 100644 --- a/examples/powertools-examples-core-utilities/terraform/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/pom.xml b/pom.xml index cb761183e..81e513b8c 100644 --- a/pom.xml +++ b/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> <groupId>software.amazon.lambda</groupId> @@ -101,7 +101,7 @@ <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.18.0</versions-maven-plugin.version> <elastic.version>1.6.0</elastic.version> - <mockito.version>5.12.0</mockito.version> + <mockito.version>5.17.0</mockito.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. --> @@ -279,6 +279,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.15.0</version> + </dependency> <!-- Test dependencies --> <dependency> @@ -294,12 +299,6 @@ <version>1.9.1</version> <scope>test</scope> </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <version>3.15.0</version> - <scope>test</scope> - </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> @@ -341,6 +340,18 @@ <version>${mockito.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <version>${mockito.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> 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 a86e515f7..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,37 +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.StorageResolution; -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); - metricsLogger.setTimestamp(currentTimeTruncatedPlusThirty); + 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.getHighResolution().equalsIgnoreCase("true") ? StorageResolution.HIGH : - StorageResolution.STANDARD)); + 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-metrics/pom.xml b/powertools-metrics/pom.xml index d51ea5b33..46f7bcd99 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> @@ -65,6 +65,10 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> <!-- Test dependencies --> <dependency> @@ -82,6 +86,11 @@ <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> @@ -92,11 +101,6 @@ <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> @@ -116,7 +120,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -125,9 +128,9 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <argLine>-Dmockito.mock.maker=subclass -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> @@ -142,7 +145,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> <scope>test</scope> </dependency> </dependencies> @@ -151,7 +153,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.10.6</version> <extensions>true</extensions> <executions> <execution> @@ -178,16 +180,26 @@ <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> + <buildArg> + --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> <buildArg> --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable </buildArg> @@ -207,5 +219,16 @@ <directory>src/main/resources</directory> </resource> </resources> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_EMF_ENVIRONMENT>Lambda</AWS_EMF_ENVIRONMENT> + </environmentVariables> + </configuration> + </plugin> + </plugins> </build> </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..d21fe163e 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,175 @@ 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 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); - String service() default ""; + /** + * Capture cold start metric without Lambda context and flush immediately + */ + default void captureColdStartMetric() { + captureColdStartMetric((DimensionSet) null); + } - boolean captureColdStart() default false; + /** + * Flush a single metric with custom 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) + */ + void flushSingleMetric(String name, double value, MetricUnit unit, String namespace, DimensionSet dimensions); - boolean raiseOnEmptyMetrics() default false; + /** + * Flush a single metric with custom 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 + */ + default void flushSingleMetric(String name, double value, MetricUnit unit, String namespace) { + flushSingleMetric(name, value, unit, namespace, null); + } } 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..1fd5f88ca --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.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 software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +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 { + private static MetricsProvider provider = new EmfMetricsProvider(); + private static Metrics metrics; + + private MetricsFactory() { + } + + /** + * Get the singleton instance of the Metrics + * + * @return the singleton Metrics instance + */ + public static synchronized Metrics getMetricsInstance() { + if (metrics == null) { + metrics = provider.getMetricsInstance(); + + // Apply default configuration from environment variables + String envNamespace = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); + if (envNamespace != null) { + metrics.setNamespace(envNamespace); + } + + // Only set Service dimension if it's not the default undefined value + String serviceName = LambdaHandlerProcessor.serviceName(); + if (!LambdaConstants.SERVICE_UNDEFINED.equals(serviceName)) { + metrics.setDefaultDimensions(DimensionSet.of("Service", serviceName)); + } + } + + return metrics; + } + + /** + * 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 instance so it will be recreated with the new provider + metrics = null; + } +} 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 6c3a89a65..000000000 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ /dev/null @@ -1,172 +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.Optional.ofNullable; -import static software.amazon.lambda.powertools.common.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; - } - - /** - * 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(); - } - } - - 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(); - if ("aws-embedded-metrics".equals(context.getNamespace())) { - String namespace = SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE"); - return namespace != null ? namespace : "aws-embedded-metrics"; - } else { - return 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..a55e1da5a --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java @@ -0,0 +1,326 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.getXrayTraceId; +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 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 TRACE_ID_PROPERTY = "xray_trace_id"; + private static final String REQUEST_ID_PROPERTY = "function_request_id"; + 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 boolean raiseOnEmptyMetrics = false; + private String namespace; + private Map<String, String> defaultDimensions = 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); + // Update our local copy of default dimensions + defaultDimensions.put(key, val); + } catch (Exception e) { + // Ignore dimension errors + } + }); + + emfLogger.putDimensions(emfDimensionSet); + } + + @Override + public void addMetadata(String key, Object value) { + emfLogger.putMetadata(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 = 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) { + throw new IllegalStateException("No metrics were emitted"); + } else { + LOGGER.warn("No metrics were emitted"); + } + } + emfLogger.flush(); + } + + @Override + public void captureColdStartMetric(Context context, + software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { + if (isColdStart()) { + if (isMetricsDisabled()) { + LOGGER.debug("Metrics are disabled, skipping cold start metric capture"); + return; + } + + Validator.validateNamespace(namespace); + + software.amazon.cloudwatchlogs.emf.logger.MetricsLogger coldStartLogger = new software.amazon.cloudwatchlogs.emf.logger.MetricsLogger(); + + try { + coldStartLogger.setNamespace(namespace); + } catch (Exception e) { + LOGGER.error("Namespace cannot be set for cold start metrics due to an error in EMF", e); + } + + coldStartLogger.putMetric(COLD_START_METRIC, 1, Unit.COUNT); + + // Set dimensions if provided + if (dimensions != null) { + DimensionSet emfDimensionSet = new DimensionSet(); + dimensions.getDimensions().forEach((key, val) -> { + try { + emfDimensionSet.addDimension(key, val); + } catch (Exception e) { + // Ignore dimension errors + } + }); + coldStartLogger.setDimensions(emfDimensionSet); + } + + // Add request ID from context if available + if (context != null) { + coldStartLogger.putProperty(REQUEST_ID_PROPERTY, context.getAwsRequestId()); + } + + // Add trace ID using the standard logic + getXrayTraceId().ifPresent(traceId -> coldStartLogger.putProperty(TRACE_ID_PROPERTY, traceId)); + + coldStartLogger.flush(); + } + } + + @Override + public void captureColdStartMetric(software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { + captureColdStartMetric(null, dimensions); + } + + @Override + public void flushSingleMetric(String name, double value, MetricUnit unit, String namespace, + software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { + if (isMetricsDisabled()) { + LOGGER.debug("Metrics are disabled, skipping single metric flush"); + return; + } + + Validator.validateNamespace(namespace); + + // Create a new logger for this single metric + software.amazon.cloudwatchlogs.emf.logger.MetricsLogger singleMetricLogger = new software.amazon.cloudwatchlogs.emf.logger.MetricsLogger( + environmentProvider); + + try { + singleMetricLogger.setNamespace(namespace); + } catch (Exception e) { + LOGGER.error("Namespace cannot be set for single metric due to an error in EMF", e); + } + + // Add the metric + singleMetricLogger.putMetric(name, value, convertUnit(unit)); + + // Set dimensions if provided + if (dimensions != null) { + DimensionSet emfDimensionSet = new DimensionSet(); + dimensions.getDimensions().forEach((key, val) -> { + try { + emfDimensionSet.addDimension(key, val); + } catch (Exception e) { + // Ignore dimension errors + } + }); + singleMetricLogger.setDimensions(emfDimensionSet); + } + + // Flush the metric + singleMetricLogger.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 56a35f67f..b214d7c52 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,129 @@ 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.common.internal.LambdaHandlerProcessor.coldStartDone; import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.extractContext; -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.serviceName; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.hasDefaultDimension; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -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 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(); - - refreshMetricsContext(metrics); - - logger.setNamespace(namespace(metrics)); - - Context extractedContext = extractContext(pjp); + // 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()); + } - if (null != extractedContext) { - coldStartSingleMetricIfApplicable(extractedContext.getAwsRequestId(), - extractedContext.getFunctionName(), metrics); - logger.putProperty(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); + // 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())); } + metricsInstance.setRaiseOnEmptyMetrics(metrics.raiseOnEmptyMetrics()); + + // Add trace ID metadata if available LambdaHandlerProcessor.getXrayTraceId() - .ifPresent(traceId -> logger.putProperty(TRACE_ID_PROPERTY, traceId)); + .ifPresent(traceId -> metricsInstance.addMetadata(TRACE_ID_PROPERTY, traceId)); + + captureColdStartMetricIfEnabled(extractContext(pjp), 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 metrics) { + if (extractedContext == null) { + return; } - } + Metrics metricsInstance = MetricsFactory.getMetricsInstance(); + metricsInstance.addMetadata(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); - 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 (metrics.captureColdStart()) { + // Get function name from annotation or context + String funcName = functionName(metrics, extractedContext); - if (dimensionsCount() > 9) { - throw new ValidationException(String.format("Number of Dimensions must be in range of 0-9." + - " Actual size: %d.", dimensionsCount())); - } - } + DimensionSet coldStartDimensions = 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 = metricsInstance.getDefaultDimensions().getDimensions().getOrDefault( + SERVICE_DIMENSION, + serviceNameWithFallback(metrics)); + + // Only add service if it is not undefined + if (!LambdaConstants.SERVICE_UNDEFINED.equals(serviceName)) { + coldStartDimensions.addDimension(SERVICE_DIMENSION, serviceName); + } + + // Add function name + coldStartDimensions.addDimension("FunctionName", + funcName != null ? funcName : extractedContext.getFunctionName()); - private void validateMetricsAndRefreshOnFailure(Metrics metrics) { - try { - validateBeforeFlushingMetrics(metrics); - } catch (ValidationException e) { - refreshMetricsContext(metrics); - throw e; + metricsInstance.captureColdStartMetric(extractedContext, coldStartDimensions); } } } 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/ValidationException.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricResolution.java similarity index 66% rename from powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java rename to powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricResolution.java index a553abbbd..db514c8b7 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/model/MetricResolution.java @@ -12,11 +12,21 @@ * */ -package software.amazon.lambda.powertools.metrics; +package software.amazon.lambda.powertools.metrics.model; -public class ValidationException extends RuntimeException { +/** + * Resolution for metrics + */ +public enum MetricResolution { + STANDARD(60), HIGH(1); + + private final int seconds; + + MetricResolution(int seconds) { + this.seconds = seconds; + } - public ValidationException(String message) { - super(message); + 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 index 8ea90d67f..75ee9e5f5 100644 --- 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 @@ -1,22 +1,33 @@ [ -{ - "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"}] -} + { + "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 index bf67fc97b..43b2822d6 100644 --- 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 @@ -1,285 +1,152 @@ [ -{ - "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.fasterxml.jackson.databind.ext.Java7SupportImpl", - "methods":[{"name":"<init>","parameterTypes":[] }] -}, -{ - "name":"com.sun.tools.attach.VirtualMachine" -}, -{ - "name":"java.io.InputStream" -}, -{ - "name":"java.io.OutputStream" -}, -{ - "name":"java.io.Serializable", - "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.Comparable", - "queryAllDeclaredMethods":true -}, -{ - "name":"java.lang.Double", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":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.Number", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true -}, -{ - "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.constant.Constable", - "queryAllDeclaredMethods":true -}, -{ - "name":"java.lang.constant.ConstantDesc", - "queryAllDeclaredMethods":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":"software.amazon.cloudwatchlogs.emf.logger.MetricsLogger", - "fields":[{"name":"context"}] -}, -{ - "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":"IS_COLD_START"}] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.MetricsLoggerTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"metricsLoggerCaptureUtilityWithDefaultNameSpace","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent","parameterTypes":[] }, {"name":"singleMetricsCaptureUtility","parameterTypes":[] }, {"name":"singleMetricsCaptureUtilityWithDefaultDimension","parameterTypes":[] }, {"name":"singleMetricsCaptureUtilityWithDefaultNameSpace","parameterTypes":[] }, {"name":"singleMetricsCaptureUtilityWithNullNamespace","parameterTypes":[] }, {"name":"tearDown","parameterTypes":[] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsColdStartEnabledHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledDefaultDimensionHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledDefaultNoDimensionHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsEnabledStreamHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsExceptionWhenNoMetricsHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsNoDimensionsHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsNoExceptionWhenNoMetricsHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsTooManyDimensionsHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsWithExceptionInHandler", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspectTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"allowWhenNoDimensionsSet","parameterTypes":[] }, {"name":"exceptionWhenNoMetricsEmitted","parameterTypes":[] }, {"name":"exceptionWhenTooManyDimensionsSet","parameterTypes":[] }, {"name":"metricsPublishedEvenHandlerThrowsException","parameterTypes":[] }, {"name":"metricsWithColdStart","parameterTypes":[] }, {"name":"metricsWithDefaultDimensionSpecified","parameterTypes":[] }, {"name":"metricsWithDefaultNoDimensionSpecified","parameterTypes":[] }, {"name":"metricsWithStreamHandler","parameterTypes":[] }, {"name":"metricsWithoutColdStart","parameterTypes":[] }, {"name":"noColdStartMetricsWhenColdStartDone","parameterTypes":[] }, {"name":"noExceptionWhenNoMetricsEmitted","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"tearDown","parameterTypes":[] }] -}, -{ - "name":"sun.reflect.ReflectionFactory", - "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] -} + { + "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": "IS_COLD_START" }], + "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 + } ] 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 index dd8fabec3..d47298855 100644 --- 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 @@ -1,19 +1,13 @@ { - "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" - }]}, - "bundles":[] + "resources": { + "includes": [ + { + "pattern": "\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, + { + "pattern": "\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + } + ] + }, + "bundles": [] } 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..1bf3b6a69 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java @@ -0,0 +1,206 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.testutils.TestContext; + +/** + * Tests to verify the hierarchy of precedence for configuration: + * 1. @FlushMetrics annotation + * 2. MetricsBuilder + * 3. Environment variables + */ +class ConfigurationPrecedenceTest { + + private final PrintStream standardOut = 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 SERVICE_NAME + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset IS_COLD_START + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(standardOut); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + 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 TestContext(); + 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 TestContext(); + 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 TestContext(); + 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 TestContext(); + 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 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 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..bd300fb6b --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java @@ -0,0 +1,186 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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.TestMetrics; +import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; + +class MetricsBuilderTest { + + private final PrintStream standardOut = 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(standardOut); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + 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() { + // Given + MetricsProvider testProvider = new TestMetricsProvider(); + + // When + Metrics metrics = MetricsBuilder.builder() + .withMetricsProvider(testProvider) + .build(); + + // Then + assertThat(metrics).isInstanceOf(TestMetrics.class); + } + + @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..962f2c2d7 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.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; + +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.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; +import software.amazon.lambda.powertools.metrics.testutils.TestMetrics; +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 final PrintStream standardOut = 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 SERVICE_NAME + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset IS_COLD_START + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(standardOut); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + 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() { + // Given + MetricsProvider testProvider = new TestMetricsProvider(); + + // When + MetricsFactory.setMetricsProvider(testProvider); + Metrics metrics = MetricsFactory.getMetricsInstance(); + + // Then + assertThat(metrics).isInstanceOf(TestMetrics.class); + } + + @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(); + } +} 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 5f99c950a..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ /dev/null @@ -1,232 +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 java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.Consumer; - -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.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; -import software.amazon.cloudwatchlogs.emf.model.StorageResolution; -import software.amazon.cloudwatchlogs.emf.model.Unit; - -@SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") -class MetricsLoggerTest { - - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - private final PrintStream originalOut = System.out; - private final ObjectMapper mapper = new ObjectMapper(); - - @BeforeEach - void setUp() { - System.setOut(new PrintStream(out)); - } - - @AfterEach - void tearDown() { - System.setOut(originalOut); - } - - @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - void singleMetricsCaptureUtilityWithDefaultDimension() { - 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 - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - void singleMetricsCaptureUtility() { - 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"); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=test"); - }); - } - - @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - void singleMetricsCaptureUtilityWithNullNamespace() { - // POWERTOOLS_METRICS_NAMESPACE is not defined - - MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, - metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=aws-embedded-metrics"); - }); - } - - @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "GlobalName") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - void singleMetricsCaptureUtilityWithDefaultNameSpace() { - 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 - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "GlobalName") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - void metricsLoggerCaptureUtilityWithDefaultNameSpace() { - testLogger(MetricsUtils::withMetricsLogger); - } - - @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "GlobalName") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - void shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent() { - 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"); - }); - } - - private void testLogger(Consumer<Consumer<MetricsLogger>> methodToTest) { - methodToTest.accept(metricsLogger -> - { - metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); - metricsLogger.putMetric("Metric1", 1, Unit.COUNT); - metricsLogger.putMetric("Metric2", 1, Unit.COUNT, StorageResolution.HIGH); - }); - - 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"); - - ArrayList cloudWatchMetrics = (ArrayList) aws.get("CloudWatchMetrics"); - LinkedHashMap<String, Object> values = - (java.util.LinkedHashMap<String, Object>) cloudWatchMetrics.get(0); - ArrayList metricArray = (ArrayList) values.get("Metrics"); - LinkedHashMap<String, Object> metricValues = (LinkedHashMap<String, Object>) metricArray.get(1); - assertThat(metricValues).containsEntry("StorageResolution", 1); - }); - } - - private Map<String, Object> readAsJson(String s) { - try { - return mapper.readValue(s, Map.class); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - return emptyMap(); - } -} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java deleted file mode 100644 index e3a0fa22e..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.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.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> { - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking", captureColdStart = true) - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - - return null; - } -} 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 fd406b9cd..000000000 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java +++ /dev/null @@ -1,40 +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(namespace = "ExampleApplication",service = "booking") - public Object handleRequest(Object input, Context context) { - MetricsLogger metricsLogger = metricsLogger(); - DimensionSet dimensionSet = new DimensionSet(); - for (int i = 0; i < 35; i++) { - dimensionSet.addDimension("Dimension" + i, "value" + i); - } - metricsLogger.setDimensions(dimensionSet); - - 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..1b7106ece --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java @@ -0,0 +1,521 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.model.Unit; +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +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.metrics.testutils.TestContext; + +class EmfMetricsLoggerTest { + + private Metrics metrics; + private final ObjectMapper objectMapper = new ObjectMapper(); + private final PrintStream standardOut = System.out; + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + + @BeforeEach + void setUp() throws Exception { + // Reset LambdaHandlerProcessor's SERVICE_NAME + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset IS_COLD_START + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); + + metrics = MetricsFactory.getMetricsInstance(); + metrics.setNamespace("TestNamespace"); + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(standardOut); + + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + field.setAccessible(true); + field.set(null, null); + } + + @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 (dimension.asText().equals("CustomDimension")) { + 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 (dimName.equals("Dim1")) { + hasDim1 = true; + } else if (dimName.equals("Dim2")) { + hasDim2 = true; + } + } + assertThat(hasDim1).isTrue(); + assertThat(hasDim2).isTrue(); + } + + @Test + void shouldThrowExceptionWhenDimensionSetIsNull() { + // When/Then + assertThatThrownBy(() -> metrics.addDimension((DimensionSet) 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.get("_aws").has("CustomMetadata")).isTrue(); + assertThat(rootNode.get("_aws").get("CustomMetadata").asText()).isEqualTo("MetadataValue"); + } + + @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((DimensionSet) 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 = new String(Files.readAllBytes(logFile.toPath()), StandardCharsets.UTF_8); + assertThat(logContent).contains("No metrics were emitted"); + } + + @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 TestContext(); + + // 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 TestContext(); + + 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 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 TestContext(); + + // 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(); + } +} 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 5df6003c8..068d19ccb 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,371 +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.when; -import static org.mockito.MockitoAnnotations.openMocks; -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.BeforeEach; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.SetEnvironmentVariable; -import org.mockito.Mock; 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.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import software.amazon.cloudwatchlogs.emf.exception.DimensionSetExceededException; import software.amazon.lambda.powertools.common.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; - -@SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") -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; +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.metrics.testutils.TestContext; + +class LambdaMetricsAspectTest { + + private final PrintStream standardOut = 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 SERVICE_NAME + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset IS_COLD_START + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + coldStartField.setAccessible(true); + coldStartField.set(null, null); } @AfterEach - void tearDown() { - System.setOut(originalOut); - } + void tearDown() throws Exception { + System.setOut(standardOut); - @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - public void metricsWithoutColdStart() { - - 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"); - }); + // Reset the singleton state between tests + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + field.setAccessible(true); + field.set(null, null); } @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - public void metricsWithDefaultDimensionSpecified() { - 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"); - }); + void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Context context = new TestContext(); + 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 - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - @SetEnvironmentVariable(key = "_X_AMZN_TRACE_ID", value = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1") - public void metricsWithDefaultNoDimensionSpecified() { - requestHandler = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); - - 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) - .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("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 TestContext(); + 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 - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void metricsWithColdStart() { - MetricsUtils.defaultDimensions(null); - requestHandler = new PowertoolsMetricsColdStartEnabledHandler(); - - requestHandler.handleRequest("input", context); - - assertThat(out.toString().split("\n")) - .hasSize(2) - .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"); - }); + @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 TestContext(); + 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"); } @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void noColdStartMetricsWhenColdStartDone() { - 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 shouldCaptureColdStartMetricWhenConfigured() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithColdStartMetricsAnnotation(); + Context context = new TestContext(); + 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("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); + + JsonNode metricsNode = objectMapper.readTree(emfOutputs[1]); + assertThat(metricsNode.has("test-metric")).isTrue(); } @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void metricsWithStreamHandler() throws IOException { - MetricsUtils.defaultDimensions(null); - RequestStreamHandler streamHandler = new PowertoolsMetricsEnabledStreamHandler(); - - streamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); - - assertThat(out.toString()) - .satisfies(s -> - { - Map<String, Object> logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + @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 TestContext(); + 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("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); + + // Service dimension should not be present in cold start metrics + assertThat(coldStartNode.has("Service")).isFalse(); + + // FunctionName dimension should be present + assertThat(coldStartNode.has("FunctionName")).isTrue(); + assertThat(coldStartNode.get("FunctionName").asText()).isEqualTo("EnvFunctionName"); } @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void exceptionWhenNoMetricsEmitted() { - 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"); + void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithCustomFunctionName(); + Context context = new TestContext(); + 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 (dimension.asText().equals("FunctionName")) { + hasFunctionName = true; + break; + } + } + assertThat(hasFunctionName).isTrue(); } @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void noExceptionWhenNoMetricsEmitted() { - 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"); - }); + void shouldUseServiceNameWhenProvidedForColdStartMetric() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithServiceNameAndColdStart(); + Context context = new TestContext(); + 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"); } @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void allowWhenNoDimensionsSet() { - 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"); - }); + void shouldHaveNoEffectOnNonHandlerMethod() { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithAnnotationOnWrongMethod(); + Context context = new TestContext(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, context); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + + // Should be empty because we do not flush any metrics manually + assertThat(emfOutput).isEmpty(); } - @Test - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void exceptionWhenTooManyDimensionsSet() { - MetricsUtils.defaultDimensions(null); + 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"; + } + } - requestHandler = new PowertoolsMetricsTooManyDimensionsHandler(); + 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"; + } + } - assertThatExceptionOfType(DimensionSetExceededException.class) - .isThrownBy(() -> requestHandler.handleRequest("input", context)) - .withMessage( - "Maximum number of dimensions allowed are 30. Account for default dimensions if not using setDimensions."); + 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 - @SetEnvironmentVariable(key = "AWS_EMF_ENVIRONMENT", value = "Lambda") - public void metricsPublishedEvenHandlerThrowsException() { - 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 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"; + } } - 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 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 Map<String, Object> readAsJson(String s) { - try { - return mapper.readValue(s, Map.class); - } catch (JsonProcessingException e) { - e.printStackTrace(); + 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"; + } + + @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/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/TestContext.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.java new file mode 100644 index 000000000..c4f5e4455 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.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.metrics.testutils; + +import com.amazonaws.services.lambda.runtime.Context; + +/** + * Simple Lambda context implementation for unit tests + */ +public class TestContext 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 "test-version"; + } + + @Override + public String getInvokedFunctionArn() { + return "test-arn"; + } + + @Override + public com.amazonaws.services.lambda.runtime.CognitoIdentity getIdentity() { + return null; + } + + @Override + public com.amazonaws.services.lambda.runtime.ClientContext getClientContext() { + return null; + } + + @Override + public int getRemainingTimeInMillis() { + return 1000; + } + + @Override + public int getMemoryLimitInMB() { + return 128; + } + + @Override + public com.amazonaws.services.lambda.runtime.LambdaLogger getLogger() { + return null; + } +} 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..949828a13 --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestMetrics.java @@ -0,0 +1,85 @@ +package software.amazon.lambda.powertools.metrics.testutils; + +import java.time.Instant; +import java.util.Collections; + +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 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/spotbugs-exclude.xml b/spotbugs-exclude.xml index e959204ad..9a42ebf16 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -234,8 +234,8 @@ <Method name="s3Client"/> </And> <And> - <Class name="software.amazon.lambda.powertools.metrics.MetricsUtils"/> - <Method name="metricsLogger"/> + <Class name="software.amazon.lambda.powertools.metrics.MetricsFactory"/> + <Method name="getMetricsInstance"/> </And> </Or> </Match> From fa1c37dfb28196f86b70164561ce25c081e25bec Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 11 Jun 2025 14:28:06 +0200 Subject: [PATCH 0672/1008] docs(metrics): Add upgrade guide for re-designed Metrics utility (#1868) * Add upgrade guide for re-designed metrics utility. Update roadmap with completed items. * Apply language suggestions. Co-authored-by: Stefano Vozza <svozza@gmail.com> --------- Co-authored-by: Stefano Vozza <svozza@gmail.com> --- docs/roadmap.md | 5 +++-- docs/upgrade.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 4a86f1437..4ef1ef06e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -17,7 +17,7 @@ Security and operational excellence take precedence above all else. This means b 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. -- [ ] Implement GitHub workflows and create infrastructure to release 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"} @@ -27,9 +27,10 @@ As part of a new major version `v2` release, we prioritize the Java project's co ##### Core Utilities -- [ ] [Review public interfaces and reduce public API surface area](https://github.com/aws-powertools/powertools-lambda-java/issues/1283){target="\_blank"} +- [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 diff --git a/docs/upgrade.md b/docs/upgrade.md index d2b1af096..d1388d95b 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -162,8 +162,53 @@ public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEve ## 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 is currently undergoing changes to the public interface as part of GitHub issue [#1848](https://github.com/aws-powertools/powertools-lambda-java/issues/1848). We will keep this upgrade guide updated with the most recent changes as soon as they are released. Stay tuned for updates! +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 From 20a4d94ae5ba0f429e1ace89edd930b78e37f1e1 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 11 Jun 2025 15:23:08 +0200 Subject: [PATCH 0673/1008] chore(v2): Remove rule preventing production release of 2.0.0 (#1867) * Remove redundant version declaration of aws-embedded-metrics.version in pom.xml * Remove maven enforcer plugin enforcing SNAPSHOT release only. --- pom.xml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/pom.xml b/pom.xml index 81e513b8c..9cfd6a7a4 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,6 @@ <maven-source-plugin.version>3.3.1</maven-source-plugin.version> <maven-gpg-plugin.version>3.2.1</maven-gpg-plugin.version> <junit.version>5.10.0</junit.version> - <aws-embedded-metrics.version>1.0.6</aws-embedded-metrics.version> <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> @@ -505,28 +504,6 @@ <id>release</id> <build> <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-enforcer-plugin</artifactId> - <version>3.5.0</version> - <executions> - <execution> - <id>enforce-snapshot-versions</id> - <phase>validate</phase> - <goals> - <goal>enforce</goal> - </goals> - <configuration> - <rules> - <requireSnapshotVersion> - <message>Release build should not have snapshot dependencies!</message> - </requireSnapshotVersion> - </rules> - <fail>true</fail> - </configuration> - </execution> - </executions> - </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> From 92926f399b090e1eacc02bd11cda5d72713aff3c Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Wed, 11 Jun 2025 15:31:01 +0200 Subject: [PATCH 0674/1008] fix(ci): Checkout repo on doc release (#1869) * fix(ci): Clone repo for docs job * version docs --- .github/workflows/release.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 826536136..54924b558 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -270,11 +270,9 @@ jobs: id-token: write environment: Docs steps: - - id: download_source - name: Download artifacts - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 - with: - name: source + - id: checkout + name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Build run: | mkdir -p dist @@ -290,4 +288,4 @@ jobs: run: | aws s3 sync \ dist \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ inputs.version }}/ From af31d6fbbb7d999f723eb2257a9c827fd2a10889 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 11 Jun 2025 16:06:51 +0200 Subject: [PATCH 0675/1008] Add Maven Central required info to pom.xml and skip deploying e2e tests and parameters tests. (#1871) --- pom.xml | 13 +++++++++++++ powertools-e2e-tests/pom.xml | 2 +- .../powertools-parameters-tests/pom.xml | 14 +++++++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 9cfd6a7a4..f00417387 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,19 @@ <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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 9cb604f56..7ec2e83e9 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -180,7 +180,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>${maven.deploy.plugin.version}</version> + <version>3.1.2</version> <configuration> <skip>true</skip> </configuration> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index e62323bd6..0120cab8f 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -12,10 +12,6 @@ <artifactId>powertools-parameters-tests</artifactId> <description>Powertools parameters tests that cut across all the parameters providers</description> - <properties> - <!-- Don't deploy the tests --> - <maven.deploy.skip>true</maven.deploy.skip> - </properties> <dependencies> <dependency> @@ -164,8 +160,16 @@ </buildArgs> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> </plugins> </build> </profile> </profiles> -</project> \ No newline at end of file +</project> From e3d4c7e131cd023b4f5568b3095a92440e56f70f Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 11 Jun 2025 16:30:18 +0200 Subject: [PATCH 0676/1008] Fix remaining maven central properties required. (#1872) --- powertools-e2e-tests/pom.xml | 53 ++++++++++--------- powertools-idempotency/pom.xml | 6 +-- .../powertools-parameters-tests/pom.xml | 19 +++++++ powertools-serialization/pom.xml | 4 +- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 7ec2e83e9..e09300001 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -174,31 +174,36 @@ </dependency> </dependencies> - <build> - <plugins> - <!-- Don't deploy the e2e tests --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.13.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.2</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.13.0</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> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index fddef497a..7ff621144 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -28,9 +28,7 @@ <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) library Idempotency</name> - <description> - - </description> + <description>Utility to implement idempotency of Lambda functions</description> <modules> @@ -116,4 +114,4 @@ </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 0120cab8f..dc3eab4c4 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -10,6 +10,7 @@ <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> @@ -72,6 +73,24 @@ </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.2</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + </profile> <profile> <id>generate-graalvm-files</id> <dependencies> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 6c45b62dd..df592476d 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -28,9 +28,7 @@ <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Serialization Utilities</name> - <description> - - </description> + <description>Utilities for JSON serialization used across the project.</description> <dependencies> <dependency> From b314321840dea0abe65bdefba509f9e6dec54da2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:07:23 +0200 Subject: [PATCH 0677/1008] chore(ci): bump version to 2.0.0-RC1 (#1875) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 40 files changed, 46 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index fd72ee148..625d3d009 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 63625214c..3462be0b7 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index b8b50b77f..275a3c96f 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 15084d9b4..edadd6005 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index a959432c0..11d5497da 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index fca756921..0b67f20ae 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.162.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 38cf96c1c..bf53c31eb 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.11.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.0.0-SNAPSHOT' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.0.0-SNAPSHOT' - aspect 'software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-tracing:2.0.0-RC1' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.0.0-RC1' + aspect 'software.amazon.lambda:powertools-metrics:2.0.0-RC1' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 3c84ce7f4..9dcac3510 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.11.3") 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.0.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-tracing:2.0.0-RC1") + aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0-RC1") + aspect("software.amazon.lambda:powertools-metrics:2.0.0-RC1") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index d25e1fcb7..011a29c60 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 2db4628b4..d43094922 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 41b5a20a1..fce13b792 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 7480bd013..dd9ed4685 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index fd673b573..f0fcf545a 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 34f469c6c..8031710b1 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index d0d8028d6..02b991d68 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 00aba4c8d..560c08a56 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index c021572f8..8bed7cb5e 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index ee5f77dde..6f185584b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -118,7 +118,7 @@ extra_javascript: extra: powertools: - version: 2.0.0-SNAPSHOT + version: 2.0.0-RC1 repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index f00417387..af17d6b1b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 819c19927..20dbd179b 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 36392fce2..e3ab012ae 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 47a505325..b9a8572ea 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e09300001..ce9e12860 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7ff621144..286ea3f97 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 59b69da0f..3012406af 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 3ab817209..e985f8f14 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index afbd0aa37..9569c9ea8 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 62cb9c352..ade7c95fc 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 96a9a7043..e85fc6567 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index a94801345..212379439 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 46f7bcd99..8ae52d43d 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 44292c755..ad8f14e36 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index f4af26062..840bb5159 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index fde6c190b..1220b1eb5 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index a652f14e0..93bd9a26e 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index ec13c10cc..1c91824e9 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index dc3eab4c4..37e266bde 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index df592476d..ec8acab9e 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 140c212e3..a869679a8 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 7d460eec3..8720509bb 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0-RC1</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 9616520ce46608da67303faa3e31fc8b07c07c21 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:32:48 +0200 Subject: [PATCH 0678/1008] chore(ci): bump version to 2.0.0 (#1876) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 40 files changed, 46 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 625d3d009..88955bca7 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 3462be0b7..6bedc015e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 275a3c96f..ef3c0e4e0 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index edadd6005..43c81b9f8 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 11d5497da..fa5a1927a 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 0b67f20ae..b869a5672 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.162.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index bf53c31eb..4cf988a6f 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.11.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.0.0-RC1' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.0.0-RC1' - aspect 'software.amazon.lambda:powertools-metrics:2.0.0-RC1' + aspect 'software.amazon.lambda:powertools-tracing:2.0.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.0.0' + aspect 'software.amazon.lambda:powertools-metrics:2.0.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 9dcac3510..7029dc458 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.11.3") 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.0.0-RC1") - aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0-RC1") - aspect("software.amazon.lambda:powertools-metrics:2.0.0-RC1") + aspect("software.amazon.lambda:powertools-tracing:2.0.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0") + aspect("software.amazon.lambda:powertools-metrics:2.0.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 011a29c60..b2353b86e 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index d43094922..813fc267f 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index fce13b792..1aea70820 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index dd9ed4685..dd4c385c0 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index f0fcf545a..c7ceabc57 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 8031710b1..f2ce0f21f 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 02b991d68..a797bbeed 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 560c08a56..b96d02b6b 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 8bed7cb5e..9b6df9783 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.0.0-RC1</version> + <version>2.0.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 6f185584b..ee6bd7322 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -118,7 +118,7 @@ extra_javascript: extra: powertools: - version: 2.0.0-RC1 + version: 2.0.0 repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs diff --git a/pom.xml b/pom.xml index af17d6b1b..951e155f6 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 20dbd179b..59e5b8c93 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index e3ab012ae..271704dea 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index b9a8572ea..dd6ef8e61 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index ce9e12860..4f1c059e9 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 286ea3f97..862cf3160 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 3012406af..58d184fc5 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index e985f8f14..b92d66dbc 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 9569c9ea8..b23e3f41c 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index ade7c95fc..75d5853e5 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index e85fc6567..75aa94a97 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 212379439..8b2a5cfd5 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 8ae52d43d..460eb220f 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index ad8f14e36..96f6f50b3 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 840bb5159..a3822d11b 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 1220b1eb5..9c7030d7c 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 93bd9a26e..99a308825 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 1c91824e9..46cf939ba 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 37e266bde..4d2b5d145 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index ec8acab9e..986a3b1d9 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index a869679a8..14d6d51b1 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 8720509bb..bfedf8d40 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0-RC1</version> + <version>2.0.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 871a844d10d8e54180737c95d1747dd3a045cc45 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 12 Jun 2025 12:24:27 +0200 Subject: [PATCH 0679/1008] docs: Version documentation (#1878) * Add versioning plugin for docs and update workflows. * Add old version banner override. Remove preview wording. --- .github/workflows/build-docs.yml | 68 ++++++++++++++++++++++---------- .github/workflows/release.yml | 55 +++++++++++++++++++++----- docs/index.md | 8 ---- docs/overrides/main.html | 8 ++++ mkdocs.yml | 9 +++-- 5 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 docs/overrides/main.html diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index d4bf75a9d..5d6870171 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,7 +1,7 @@ -# Build Docs +# Build Latest Docs # # Description: -# Builds the docs and stores them in S3 to be served by our docs platform +# 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 @@ -15,17 +15,13 @@ on: workflow_dispatch: inputs: - alias: + version: + description: "Version to build and publish docs (1.28.0, develop)" + required: true type: string - required: false - description: | - Alias to deploy the documentation into, this is mostly for testing pre-release - versions of the documentation, such as beta versions or snapshots. - https://docs.powertools.aws.dev/lambda/java/<alias> - -name: Build Docs -run-name: Build Docs - ${{ contains(github.head_ref, 'main') && 'main' || inputs.alias }} +name: Build Latest Docs +run-name: Build Latest Docs - ${{ inputs.version }} jobs: docs: @@ -35,28 +31,58 @@ jobs: id-token: write environment: Docs steps: - - name: Sanity Check - if: ${{ github.head_ref != 'main' || inputs.alias == '' }} - run: - echo "::error::No buildable docs" - - name: Checkout Repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - with: + with: fetch-depth: 0 - name: Build run: | mkdir -p dist docker build -t squidfunk/mkdocs-material ./docs/ docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build - cp -R site/* dist/ - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} - - name: Deploy + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" run: | aws s3 sync \ - dist \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ github.head_ref == 'main' && '' || format('{0}/', inputs.alias )}} \ No newline at end of file + 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/release.yml b/.github/workflows/release.yml index 54924b558..2fa4770c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ # # Triggers: # - workflow_dispatch -# +# # Secrets: # - RELEASE.GPG_SIGNING_KEY # - RELEASE.OSSRH_JIRA_USERNAME @@ -39,7 +39,7 @@ on: type: boolean description: Create snapshot release default: false - skip_checks: + skip_checks: type: boolean description: Skip quality checks default: false @@ -47,7 +47,7 @@ on: type: boolean description: Skip publish to Maven Central default: false - continue_on_error: + continue_on_error: type: boolean description: Continue to build if there's an error in quality checks default: false @@ -55,7 +55,7 @@ on: name: Release run-name: Release – ${{ inputs.version }} -permissions: +permissions: contents: read env: @@ -124,7 +124,7 @@ jobs: quality: runs-on: ubuntu-latest - needs: + needs: - version_seal if: ${{ inputs.skip_checks == false }} permissions: @@ -211,7 +211,7 @@ jobs: 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') }} @@ -278,14 +278,49 @@ jobs: mkdir -p dist docker build -t squidfunk/mkdocs-material ./docs/ docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build - cp -R site/* dist/ - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} - - name: Deploy + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' run: | aws s3 sync \ - dist \ - s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ inputs.version }}/ + 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/docs/index.md b/docs/index.md index 43d1ea03b..3c6b9506b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,14 +3,6 @@ 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/publish-v2-snapshot.yml/badge.svg) ![Maven Central](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Faws.oss.sonatype.org%2Fcontent%2Frepositories%2Fsnapshots%2Fsoftware%2Famazon%2Flambda%2Fpowertools-parent%2Fmaven-metadata.xml -) - -???+ warning - You are browsing the documentation for Powertools for AWS Lambda (Java) - v2. This is a snapshot release and not stable! - Check out our stable [v1](https://docs.powertools.aws.dev/lambda/java/) documentation if this is not what you wanted. - The v2 maven snapshot repository can be found [here](https://aws.oss.sonatype.org/content/repositories/snapshots/software/amazon/lambda/) . - 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. ???+ tip diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..e4c38e21b --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,8 @@ +{% 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 %} diff --git a/mkdocs.yml b/mkdocs.yml index ee6bd7322..82a32d49c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ -site_name: Powertools for AWS Lambda (Java) Preview -site_description: Powertools for AWS Lambda (Java) Preview +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/preview/ +site_url: https://docs.powertools.aws.dev/lambda/java/ nav: - Homepage: index.md - Changelog: changelog.md @@ -119,6 +119,9 @@ extra_javascript: extra: powertools: version: 2.0.0 + version: + provider: mike + default: latest repo_url: https://github.com/aws-powertools/powertools-lambda-java edit_uri: edit/main/docs From 0b275948638450165281a1d7abb5b4d321f3aeec Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Fri, 13 Jun 2025 11:05:23 +0200 Subject: [PATCH 0680/1008] chore(ci): Update workflows to make v2 the default (#1888) * chore(ci): Make v2 the main branch * update runner size for release * update skip checks * create tag instead of release --- .github/auto_assign-issues.yml | 9 --------- .github/workflows/check-build.yml | 1 - .github/workflows/check-e2e.yml | 1 - .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release-drafter.yml | 5 ----- .github/workflows/release.yml | 17 ++++++++--------- .../workflows/security-branch-protections.yml | 2 +- .github/workflows/security-dependabot.yml | 2 +- .github/workflows/security-osv.yml | 2 -- 9 files changed, 11 insertions(+), 30 deletions(-) delete mode 100644 .github/auto_assign-issues.yml 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/workflows/check-build.yml b/.github/workflows/check-build.yml index cc5931d05..b5fe372dc 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -38,7 +38,6 @@ on: push: branches: - main - - v2 paths: # add other modules when there are under e2e tests - 'powertools-batch/**' - 'powertools-core/**' diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 14eab5394..d5c95f156 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -15,7 +15,6 @@ on: push: branches: - main - - v2 paths: # add other modules when there are under e2e tests - 'powertools-batch/**' - 'powertools-core/**' diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index 0749dfaa0..fd76d9560 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -9,7 +9,7 @@ on: pull_request: branches: - - v2 + - main paths: - 'powertools-batch/**' - 'powertools-core/**' diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index f727ee25d..39d453ced 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -15,16 +15,11 @@ name: Release Drafter run-name: Release Drafter jobs: -<<<<<<< HEAD - update_release_draft: - runs-on: ubuntu-latest -======= update_release: runs-on: ubuntu-latest permissions: contents: write id-token: write ->>>>>>> 4a17172a (chore(automation): Update automation workflows (#1779)) steps: - name: Relase Drafter uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2fa4770c2..dc462fbfc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,7 +82,7 @@ jobs: - id: base name: Base run: | - echo build_version=$(test ${{ github.ref_name }} == "v2" && echo "v2" || echo "v1") >> $GITHUB_OUTPUT + 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' }} @@ -123,7 +123,7 @@ jobs: retention-days: 1 quality: - runs-on: ubuntu-latest + runs-on: aws-powertools_ubuntu-latest_8-core needs: - version_seal if: ${{ inputs.skip_checks == false }} @@ -156,7 +156,7 @@ jobs: uploadSarifReport: false build: - runs-on: ubuntu-latest + runs-on: aws-powertools_ubuntu-latest_8-core needs: - setup - quality @@ -183,8 +183,8 @@ jobs: mvn -B install --file pom.xml publish: - runs-on: ubuntu-latest - if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' && inputs.skip_publish == false }} + 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 @@ -254,11 +254,10 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - id: tag - name: Create release + name: Create tag run: | - gh release create v${{ inputs.version }} --target $(git rev-parse HEAD) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + git tag -a v${{ inputs.version }} -m "Release v${{ inputs.version }}" + git push origin v${{ inputs.version }} docs: runs-on: ubuntu-latest diff --git a/.github/workflows/security-branch-protections.yml b/.github/workflows/security-branch-protections.yml index dc7c06316..05a082b0b 100644 --- a/.github/workflows/security-branch-protections.yml +++ b/.github/workflows/security-branch-protections.yml @@ -43,7 +43,7 @@ jobs: # List of branches we want to monitor for protection changes branch: - main - - v2 + - v1 steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/security-dependabot.yml b/.github/workflows/security-dependabot.yml index 095219045..e1422fb2b 100644 --- a/.github/workflows/security-dependabot.yml +++ b/.github/workflows/security-dependabot.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'aws-powertools/powertools-lambda-java' }} permissions: - pull-requests: read + pull-requests: write steps: - id: dependabot-metadata name: Fetch Dependabot metadata diff --git a/.github/workflows/security-osv.yml b/.github/workflows/security-osv.yml index b332faae3..67e2e6e3f 100644 --- a/.github/workflows/security-osv.yml +++ b/.github/workflows/security-osv.yml @@ -13,14 +13,12 @@ on: pull_request: branches: - main - - v2 workflow_dispatch: {} schedule: - cron: "30 12 * * 1" push: branches: - main - - v2 name: OpenSource Vulnerability Scanner run-name: OpenSource Vulnerability Scanner From cc1b3b11d0118f35bbc22dd318693e05fec87e71 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 16 Jun 2025 16:59:54 +0200 Subject: [PATCH 0681/1008] docs: Announce deprecation of v1 --- docs/processes/versioning.md | 7 ++++--- docs/upgrade.md | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/processes/versioning.md b/docs/processes/versioning.md index 8b12e0fa9..bbb60f507 100644 --- a/docs/processes/versioning.md +++ b/docs/processes/versioning.md @@ -55,6 +55,7 @@ To see the list of available major versions of Powertools for AWS Lambda and whe ### Version support matrix -| SDK | Major version | Current Phase | General Availability Date | Notes | -| -------------------------------- | ------------- | -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | -| Powertools for AWS Lambda (Java) | 1.x | General Availability | 11/04/2020 | See [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v1.0.0) | +| 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 | Maintenance | 11/04/2020 | End-of-support: December 12, 2025. See [upgrade guide](https://docs.powertools.aws.dev/lambda/java/latest/upgrade/) | diff --git a/docs/upgrade.md b/docs/upgrade.md index d1388d95b..5b6d16d99 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -5,7 +5,10 @@ description: Guide to update between major Powertools for AWS Lambda (Java) vers ## End of support v1 -<!-- TODO: Add end of support banner here once developer preview started. --> +<!-- prettier-ignore-start --> +!!! warning "End of support notice" + On December 12th, 2025, Powertools for AWS Lambda (Java) v1 will reach end of support 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. +<!-- 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. From 8a040ac87a2a31662b7ac8980601b4d821cc29d8 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 16 Jun 2025 17:00:49 +0200 Subject: [PATCH 0682/1008] fix(metrics): Do not flush when no metrics were added to avoid printing root-level _aws dict (#1891) * fix(metrics): Do not flush when no metrics were added to avoid printing root-level _aws dict. * Fix pmd linting failures. --- .../powertools/metrics/internal/EmfMetricsLogger.java | 3 ++- .../metrics/internal/EmfMetricsLoggerTest.java | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) 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 index a55e1da5a..1eedd270d 100644 --- 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 @@ -164,8 +164,9 @@ public void flush() { } else { LOGGER.warn("No metrics were emitted"); } + } else { + emfLogger.flush(); } - emfLogger.flush(); } @Override 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 index 1b7106ece..a4fc0d61c 100644 --- 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 @@ -51,7 +51,7 @@ class EmfMetricsLoggerTest { private Metrics metrics; private final ObjectMapper objectMapper = new ObjectMapper(); - private final PrintStream standardOut = System.out; + private static final PrintStream standardOut = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); @BeforeEach @@ -180,7 +180,7 @@ void shouldAddDimension() throws Exception { JsonNode dimensions = rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); boolean hasDimension = false; for (JsonNode dimension : dimensions) { - if (dimension.asText().equals("CustomDimension")) { + if ("CustomDimension".equals(dimension.asText())) { hasDimension = true; break; } @@ -233,9 +233,9 @@ void shouldAddDimensionSet() throws Exception { boolean hasDim2 = false; for (JsonNode dimension : dimensions) { String dimName = dimension.asText(); - if (dimName.equals("Dim1")) { + if ("Dim1".equals(dimName)) { hasDim1 = true; - } else if (dimName.equals("Dim2")) { + } else if ("Dim2".equals(dimName)) { hasDim2 = true; } } @@ -348,6 +348,8 @@ void shouldLogWarningOnEmptyMetrics() throws Exception { // Read the log file and check for the warning String logContent = new String(Files.readAllBytes(logFile.toPath()), StandardCharsets.UTF_8); assertThat(logContent).contains("No metrics were emitted"); + // No EMF output should be generated + assertThat(outputStreamCaptor.toString().trim()).isEmpty(); } @Test From eebc06ae150d4b04cc48bea67785ab019c7258a1 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 18 Jun 2025 19:02:43 +0200 Subject: [PATCH 0683/1008] feat(kafka): New Kafka utility (#1898) * Add initial code for KafkaJson and KafkaAvro request handlers. * Add deserialization via @Deserialization annotation. * Add TODOs in code. * Fix typos and make AbstractKafkaDeserializer package private. * Remove request handler implementation in favor for @Deserialization annotation. * Parse Timestamp type correctly. * Remove custom RequestHandler implementation example. * Make AspectJ version compatible with min version Java 11. * Clarify exception message when deserialization fails. * Add more advanced JSON escpaing to JSONSerializer in logging module. * Add protobuf deserialization logic and fully working example. * Add Maven profile to compile a JAR with different dependency combinations. * Add minimal kafka example. * Add missing copyright. * Add unit tests for kafka utility. * Add minimal kafka example to examples module in pom.xml. * Add some comments. * Update powertools-examples-kafka with README and make it more minimalistic. Remove powertools-examples-kafka-minimal. * Implement PR feedback from Karthik. * Fix SAM outputs. * Do not fail on unknown properties when deserializating into KafkaEvent. * Allow customers to bring their own kafka-clients dependency. * Add Kafka utility documentation. * Update project version consistently to 2.0.0. * fix: Fix bug where abbreviated _HANDLER env var did not detect the Deserialization annotation. * fix: Bug when trying to deserialize a type into itself for Lambda default behavior. We can just return the type itself. Relevant for simple String and InputStream handlers. * When falling back to Lambda default, handle conversion between InputStream and String. * Raise a runtime exception when the KafkaEvent is invalid. * docs: Announce deprecation of v1 * fix(metrics): Do not flush when no metrics were added to avoid printing root-level _aws dict (#1891) * fix(metrics): Do not flush when no metrics were added to avoid printing root-level _aws dict. * Fix pmd linting failures. * Rename docs to Kafka Consumer and add line highlights for code examples. * Fix Spotbug issues. * Reduce cognitive complexity of DeserializationUtils making it more modular and representing handler information in a simple HandlerInfo class. * Reduce cognitive complexity of AbstractKafkaDeserializer. * Enable removal policy DESTROY on e2e test for kinesis streams and SQS queues to avoid exceeding account limit. * Replace System.out with Powertools Logging. * Add notice about kafka-clients compatibility. * Add sentence stating that Avro / Protobuf classes can be autogenerated. --- docs/utilities/kafka.md | 1001 +++++++++++++++++ examples/pom.xml | 3 +- examples/powertools-examples-kafka/README.md | 77 ++ .../events/kafka-avro-event.json | 51 + .../events/kafka-json-event.json | 51 + .../events/kafka-protobuf-event.json | 51 + examples/powertools-examples-kafka/pom.xml | 232 ++++ .../src/main/avro/AvroProduct.avsc | 10 + .../kafka/AvroDeserializationFunction.java | 37 + .../kafka/JsonDeserializationFunction.java | 35 + .../src/main/java/org/demo/kafka/Product.java | 63 ++ .../ProtobufDeserializationFunction.java | 38 + .../java/org/demo/kafka/avro/AvroProduct.java | 476 ++++++++ .../demo/kafka/protobuf/ProtobufProduct.java | 636 +++++++++++ .../protobuf/ProtobufProductOrBuilder.java | 36 + .../protobuf/ProtobufProductOuterClass.java | 63 ++ .../src/main/proto/ProtobufProduct.proto | 13 + .../src/main/resources/log4j2.xml | 16 + .../powertools-examples-kafka/template.yaml | 59 + .../powertools-examples-kafka/tools/README.md | 66 ++ .../powertools-examples-kafka/tools/pom.xml | 104 ++ .../java/org/demo/kafka/avro/AvroProduct.java | 476 ++++++++ .../demo/kafka/protobuf/ProtobufProduct.java | 636 +++++++++++ .../protobuf/ProtobufProductOrBuilder.java | 36 + .../protobuf/ProtobufProductOuterClass.java | 63 ++ .../demo/kafka/tools/GenerateAvroSamples.java | 121 ++ .../demo/kafka/tools/GenerateJsonSamples.java | 126 +++ .../kafka/tools/GenerateProtobufSamples.java | 125 ++ mkdocs.yml | 7 +- pom.xml | 7 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- .../handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 4 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- .../handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- .../powertools/testutils/Infrastructure.java | 2 + powertools-kafka/pom.xml | 223 ++++ .../powertools/kafka/Deserialization.java | 31 + .../powertools/kafka/DeserializationType.java | 17 + .../kafka/PowertoolsSerializer.java | 67 ++ .../kafka/internal/DeserializationUtils.java | 96 ++ .../AbstractKafkaDeserializer.java | 294 +++++ .../serializers/KafkaAvroDeserializer.java | 46 + .../serializers/KafkaJsonDeserializer.java | 29 + .../KafkaProtobufDeserializer.java | 43 + .../LambdaDefaultDeserializer.java | 65 ++ .../serializers/PowertoolsDeserializer.java | 27 + ...rvices.lambda.runtime.CustomPojoSerializer | 1 + .../src/test/avro/TestProduct.avsc | 10 + .../powertools/kafka/DeserializationTest.java | 71 ++ .../kafka/DeserializationTypeTest.java | 50 + .../kafka/PowertoolsSerializerTest.java | 417 +++++++ .../internal/DeserializationUtilsTest.java | 145 +++ .../AbstractKafkaDeserializerTest.java | 473 ++++++++ .../KafkaAvroDeserializerTest.java | 73 ++ .../KafkaJsonDeserializerTest.java | 66 ++ .../KafkaProtobufDeserializerTest.java | 75 ++ .../kafka/testutils/AvroHandler.java | 30 + .../kafka/testutils/DefaultHandler.java | 29 + .../kafka/testutils/InputStreamHandler.java | 30 + .../kafka/testutils/JsonHandler.java | 29 + .../kafka/testutils/ProtobufHandler.java | 30 + .../kafka/testutils/StringHandler.java | 23 + .../kafka/testutils/TestProductPojo.java | 87 ++ .../powertools/kafka/testutils/TestUtils.java | 75 ++ .../src/test/proto/TestProduct.proto | 13 + .../test/resources/simplelogger.properties | 13 + .../internal/LambdaEcsEncoderTest.java | 2 +- .../internal/LambdaJsonEncoderTest.java | 2 +- .../logging/internal/JsonSerializer.java | 11 +- 76 files changed, 7413 insertions(+), 21 deletions(-) create mode 100644 docs/utilities/kafka.md create mode 100644 examples/powertools-examples-kafka/README.md create mode 100644 examples/powertools-examples-kafka/events/kafka-avro-event.json create mode 100644 examples/powertools-examples-kafka/events/kafka-json-event.json create mode 100644 examples/powertools-examples-kafka/events/kafka-protobuf-event.json create mode 100644 examples/powertools-examples-kafka/pom.xml create mode 100644 examples/powertools-examples-kafka/src/main/avro/AvroProduct.avsc create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/AvroDeserializationFunction.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/JsonDeserializationFunction.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/Product.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/ProtobufDeserializationFunction.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/avro/AvroProduct.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java create mode 100644 examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java create mode 100644 examples/powertools-examples-kafka/src/main/proto/ProtobufProduct.proto create mode 100644 examples/powertools-examples-kafka/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-kafka/template.yaml create mode 100644 examples/powertools-examples-kafka/tools/README.md create mode 100644 examples/powertools-examples-kafka/tools/pom.xml create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/avro/AvroProduct.java create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateAvroSamples.java create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateJsonSamples.java create mode 100644 examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateProtobufSamples.java create mode 100644 powertools-kafka/pom.xml create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/Deserialization.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializer.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtils.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializer.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializer.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializer.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializer.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/LambdaDefaultDeserializer.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/PowertoolsDeserializer.java create mode 100644 powertools-kafka/src/main/resources/META-INF/services/com.amazonaws.services.lambda.runtime.CustomPojoSerializer create mode 100644 powertools-kafka/src/test/avro/TestProduct.avsc create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/DeserializationTypeTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/DeserializationUtilsTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializerTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializerTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializerTest.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/AvroHandler.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/DefaultHandler.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/JsonHandler.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/ProtobufHandler.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/StringHandler.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestProductPojo.java create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/TestUtils.java create mode 100644 powertools-kafka/src/test/proto/TestProduct.proto create mode 100644 powertools-kafka/src/test/resources/simplelogger.properties 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/examples/pom.xml b/examples/pom.xml index 6bedc015e..ea1e8d542 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -39,6 +39,7 @@ <module>powertools-examples-parameters/sam</module> <module>powertools-examples-parameters/sam-graalvm</module> <module>powertools-examples-serialization</module> + <module>powertools-examples-kafka</module> <module>powertools-examples-batch</module> <module>powertools-examples-validation</module> <module>powertools-examples-cloudformation</module> @@ -58,4 +59,4 @@ </plugins> </build> -</project> \ No newline at end of file +</project> 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..b3e0139e3 --- /dev/null +++ b/examples/powertools-examples-kafka/events/kafka-protobuf-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": "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": "COoHEgpTbWFydHBob25lGVK4HoXrv4JA", + "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": "COsHEgpIZWFkcGhvbmVzGUjhehSuv2JA", + "headers": [ + { + "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] + } + ] + } + ] + } +} diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml new file mode 100644 index 000000000..a745ac75d --- /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.0.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.0</avro.version> + <protobuf.version>4.31.0</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.0.0</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.0</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</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.3.0</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..6da9113fc --- /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.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/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..9c1518db3 --- /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.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/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..6a99f35ec --- /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.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/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-kafka/src/main/resources/log4j2.xml b/examples/powertools-examples-kafka/src/main/resources/log4j2.xml new file mode 100644 index 000000000..fe943d707 --- /dev/null +++ b/examples/powertools-examples-kafka/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-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..53d07b0c4 --- /dev/null +++ b/examples/powertools-examples-kafka/tools/README.md @@ -0,0 +1,66 @@ +# 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`. + +## 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 + +## 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..97231e5bd --- /dev/null +++ b/examples/powertools-examples-kafka/tools/pom.xml @@ -0,0 +1,104 @@ +<?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> + </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>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.3.0</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..4bd6ebd13 --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateAvroSamples.java @@ -0,0 +1,121 @@ +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 class GenerateAvroSamples { + + 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..a4fd6565a --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateJsonSamples.java @@ -0,0 +1,126 @@ +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 class GenerateJsonSamples { + + 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..ae078a28a --- /dev/null +++ b/examples/powertools-examples-kafka/tools/src/main/java/org/demo/kafka/tools/GenerateProtobufSamples.java @@ -0,0 +1,125 @@ +package org.demo.kafka.tools; + +import java.io.IOException; +import java.util.Base64; + +import org.demo.kafka.protobuf.ProtobufProduct; + +/** + * Utility class to generate base64-encoded Protobuf serialized products + * for use in test events. + */ +public class GenerateProtobufSamples { + + public static void main(String[] args) throws IOException { + // Create three different products + ProtobufProduct product1 = ProtobufProduct.newBuilder() + .setId(1001) + .setName("Laptop") + .setPrice(999.99) + .build(); + + ProtobufProduct product2 = ProtobufProduct.newBuilder() + .setId(1002) + .setName("Smartphone") + .setPrice(599.99) + .build(); + + ProtobufProduct product3 = ProtobufProduct.newBuilder() + .setId(1003) + .setName("Headphones") + .setPrice(149.99) + .build(); + + // 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 Protobuf products for use in kafka-protobuf-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(ProtobufProduct product) { + return Base64.getEncoder().encodeToString(product.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 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/mkdocs.yml b/mkdocs.yml index 82a32d49c..07be3c175 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,8 +15,9 @@ 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 @@ -101,8 +102,9 @@ plugins: 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 @@ -115,6 +117,7 @@ extra_css: extra_javascript: - javascript/aws-amplify.min.js - javascript/extra.js + - https://docs.powertools.aws.dev/shared/mermaid.min.js extra: powertools: diff --git a/pom.xml b/pom.xml index 951e155f6..f27ffc497 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ <modules> <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> @@ -113,7 +114,9 @@ <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.18.0</versions-maven-plugin.version> <elastic.version>1.6.0</elastic.version> - <mockito.version>5.17.0</mockito.version> + <mockito.version>5.18.0</mockito.version> + <mockito-junit-jupiter.version>5.18.0</mockito-junit-jupiter.version> + <junit-pioneer.version>2.3.0</junit-pioneer.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. --> @@ -355,7 +358,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> - <version>${mockito.version}</version> + <version>${mockito-junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index a36d464ea..3b7238b4e 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index e3a67a5b5..dfa97225a 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 0728404bf..ce3fbbdd5 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index b57063346..e9e87da2b 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 88feda09b..62f2f7530 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-logging</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 68059e67e..e543c2cd0 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 2d6a9a06a..471e79d8f 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index b55cf436a..988ae3d55 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,13 +4,13 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.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>2.0.0-SNAPSHOT</lambda.powertools.version> + <lambda.powertools.version>2.0.0</lambda.powertools.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index b96fcef0a..b1bc14c05 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index be50094c1..36695b9a4 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index f204a8a9f..8bb927778 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>2.0.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> 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 143409989..07d816112 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 @@ -290,6 +290,7 @@ private Stack createStackWithLambda() { .queueName(queue) .visibilityTimeout(Duration.seconds(timeout * 6)) .retentionPeriod(Duration.seconds(timeout * 6)) + .removalPolicy(RemovalPolicy.DESTROY) .build(); DeadLetterQueue.builder() .queue(sqsQueue) @@ -314,6 +315,7 @@ private Stack createStackWithLambda() { .create(e2eStack, "KinesisStream") .streamMode(StreamMode.ON_DEMAND) .streamName(kinesisStream) + .removalPolicy(RemovalPolicy.DESTROY) .build(); stream.grantRead(function); diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml new file mode 100644 index 000000000..f5b80012c --- /dev/null +++ b/powertools-kafka/pom.xml @@ -0,0 +1,223 @@ +<?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.0.0</version> + </parent> + + <artifactId>powertools-kafka</artifactId> + <packaging>jar</packaging> + + <name>Powertools for AWS Lambda (Java) - Kafka Consumer</name> + <description></description> + + <properties> + <kafka-clients.version>4.0.0</kafka-clients.version> + <avro.version>1.12.0</avro.version> + <protobuf.version>4.31.0</protobuf.version> + <lambda-serialization.version>1.1.5</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> + + <!-- 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> + </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.3.0</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.0</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> + +</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-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java new file mode 100644 index 000000000..a4ac95389 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/DeserializationType.java @@ -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. + */ +package software.amazon.lambda.powertools.kafka; + +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..be8563b8e --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializer.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.kafka; + +import java.io.InputStream; +import java.io.OutputStream; +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 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 { + 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; + + 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); + } +} 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/serializers/AbstractKafkaDeserializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializer.java new file mode 100644 index 000000000..8d0fc8f61 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializer.java @@ -0,0 +1,294 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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); + + /** + * 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"); + V value = deserializeField(eventRecord.getValue(), valueType, "value"); + 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) { + if (encodedData == null) { + return null; + } + + try { + byte[] decodedBytes = Base64.getDecoder().decode(encodedData); + return deserialize(decodedBytes, type); + } 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; + } + + /** + * Template method to be implemented by subclasses for specific deserialization logic + * for complex types (non-primitives). + * + * @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 + * @return The deserialized object + * @throws IOException If deserialization fails + */ + protected abstract <T> T deserializeObject(byte[] data, Class<T> type) throws IOException; + + /** + * Main deserialize method that handles primitive types and delegates to subclasses for complex types. + * + * @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 object + * @throws IOException If deserialization fails + */ + private <T> T deserialize(byte[] data, Class<T> type) 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); + } + + /** + * 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..ddf09d4ff --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializer.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.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) 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..ed64f3786 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializer.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.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) 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..025f203c4 --- /dev/null +++ b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializer.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.kafka.serializers; + +import java.io.IOException; +import com.google.protobuf.Message; +import com.google.protobuf.Parser; + +/** + * Deserializer for Kafka records using Protocol Buffers format. + */ +public class KafkaProtobufDeserializer extends AbstractKafkaDeserializer { + + @Override + @SuppressWarnings("unchecked") + protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException { + // If no Protobuf generated class is passed we cannot deserialize using Protobuf + if (Message.class.isAssignableFrom(type)) { + try { + // Get the parser from the generated Protobuf class + Parser<Message> parser = (Parser<Message>) type.getMethod("parser").invoke(null); + Message message = parser.parseFrom(data); + return type.cast(message); + } 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."); + } + } +} 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/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..6ce57ecd5 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java @@ -0,0 +1,417 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.assertThatThrownBy; +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.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 { + // 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) { + 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 { + // 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) { + 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"; + ByteArrayInputStream inputStream = new ByteArrayInputStream(testInput.getBytes()); + + // This should return the input stream directly + 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() { + // When + LambdaDefaultDeserializer deserializer = new LambdaDefaultDeserializer(); + + // Then + String expected = "This is a test string"; + 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 + 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"); + } + + @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 JsonProcessingException { + // 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) { + 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) { + 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) { + // 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) { + 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 + } +} 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/serializers/AbstractKafkaDeserializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java new file mode 100644 index 000000000..512058bca --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java @@ -0,0 +1,473 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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"); + } + } + + // Test implementation of AbstractKafkaDeserializer + private static class TestDeserializer extends AbstractKafkaDeserializer { + @Override + protected <T> T deserializeObject(byte[] data, Class<T> type) 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..a0b59b136 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaAvroDeserializerTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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)) + .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); + + // 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)) + .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..0cfb2498b --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaJsonDeserializerTest.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.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)) + .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); + + // 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..2d506de4b --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/KafkaProtobufDeserializerTest.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.serializers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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)) + .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); + + // 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)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Failed to deserialize Protobuf data"); + } +} 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-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.java new file mode 100644 index 000000000..63e225ab8 --- /dev/null +++ b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/testutils/InputStreamHandler.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 java.io.IOException; +import java.io.InputStream; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +public class InputStreamHandler implements RequestHandler<InputStream, String> { + @Override + 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-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 index 5dcca2fb2..7e8977508 100644 --- 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 @@ -150,7 +150,7 @@ void shouldLogException() { 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"); + 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() { 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 index 4a7067540..81e830045 100644 --- 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 @@ -419,7 +419,7 @@ void shouldLogException() { // 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"); + .contains("\"stack\":\"java.lang.IllegalStateException: Unexpected value\\n"); } private void setupContext() { 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 index 82bc76a38..c69789519 100644 --- 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 @@ -84,8 +84,15 @@ public void writeString(String text) { if (text == null) { writeNull(); } else { - // Escape double quotes to avoid breaking JSON format - builder.append("\"").append(text.replace("\"", "\\\"")).append("\""); + // 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("\""); } } From f563d2349d5a569d5a6996d9404023fdceed64a3 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 19 Jun 2025 15:09:17 +0200 Subject: [PATCH 0684/1008] fix(kafka): Add support for confluent message indices. (#1902) * fix(kafka): Add support for confluent message indices. * Make Generator classes non-instantiable (they are static utility classes). * Make generator classes final. --- .../events/kafka-protobuf-event.json | 4 +- .../powertools-examples-kafka/tools/README.md | 9 +- .../demo/kafka/tools/GenerateAvroSamples.java | 34 ++--- .../demo/kafka/tools/GenerateJsonSamples.java | 6 +- .../kafka/tools/GenerateProtobufSamples.java | 117 ++++++++++++------ .../KafkaProtobufDeserializer.java | 58 ++++++++- .../KafkaProtobufDeserializerTest.java | 77 ++++++++++++ 7 files changed, 246 insertions(+), 59 deletions(-) diff --git a/examples/powertools-examples-kafka/events/kafka-protobuf-event.json b/examples/powertools-examples-kafka/events/kafka-protobuf-event.json index b3e0139e3..e0547ad88 100644 --- a/examples/powertools-examples-kafka/events/kafka-protobuf-event.json +++ b/examples/powertools-examples-kafka/events/kafka-protobuf-event.json @@ -25,7 +25,7 @@ "timestamp": 1545084650988, "timestampType": "CREATE_TIME", "key": "NDI=", - "value": "COoHEgpTbWFydHBob25lGVK4HoXrv4JA", + "value": "AAjpBxIGTGFwdG9wGVK4HoXrP49A", "headers": [ { "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] @@ -39,7 +39,7 @@ "timestamp": 1545084650989, "timestampType": "CREATE_TIME", "key": null, - "value": "COsHEgpIZWFkcGhvbmVzGUjhehSuv2JA", + "value": "AgEACOkHEgZMYXB0b3AZUrgehes/j0A=", "headers": [ { "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] diff --git a/examples/powertools-examples-kafka/tools/README.md b/examples/powertools-examples-kafka/tools/README.md index 53d07b0c4..02e8dde9b 100644 --- a/examples/powertools-examples-kafka/tools/README.md +++ b/examples/powertools-examples-kafka/tools/README.md @@ -45,7 +45,7 @@ The tool will output base64-encoded values for Avro products that can be used in 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`. +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 @@ -55,6 +55,13 @@ Each generator produces: 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: 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 index 4bd6ebd13..e6f4d38fd 100644 --- 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 @@ -14,62 +14,68 @@ * Utility class to generate base64-encoded Avro serialized products * for use in test events. */ -public class GenerateAvroSamples { +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" + + " \"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" + 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 index a4fd6565a..d0ef7cb55 100644 --- 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 @@ -11,7 +11,11 @@ * Utility class to generate base64-encoded JSON serialized products * for use in test events. */ -public class GenerateJsonSamples { +public final class GenerateJsonSamples { + + private GenerateJsonSamples() { + // Utility class + } public static void main(String[] args) throws IOException { // Create three different products 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 index ae078a28a..eecd3e1cc 100644 --- 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 @@ -1,73 +1,110 @@ package org.demo.kafka.tools; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Base64; 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 class GenerateProtobufSamples { +public final class GenerateProtobufSamples { + + private GenerateProtobufSamples() { + // Utility class + } public static void main(String[] args) throws IOException { - // Create three different products - ProtobufProduct product1 = ProtobufProduct.newBuilder() + // Create a single product that will be used for all three scenarios + ProtobufProduct product = ProtobufProduct.newBuilder() .setId(1001) .setName("Laptop") .setPrice(999.99) .build(); - ProtobufProduct product2 = ProtobufProduct.newBuilder() - .setId(1002) - .setName("Smartphone") - .setPrice(599.99) - .build(); - - ProtobufProduct product3 = ProtobufProduct.newBuilder() - .setId(1003) - .setName("Headphones") - .setPrice(149.99) - .build(); - - // Serialize and encode each product - String encodedProduct1 = serializeAndEncode(product1); - String encodedProduct2 = serializeAndEncode(product2); - String encodedProduct3 = serializeAndEncode(product3); + // Create three different serializations of the same product + String standardProduct = serializeAndEncode(product); + String productWithSimpleIndex = serializeWithSimpleMessageIndex(product); + String productWithComplexIndex = serializeWithComplexMessageIndex(product); - // Serialize and encode an integer key + // Serialize and encode an integer key (same for all records) String encodedKey = serializeAndEncodeInteger(42); // Print the results - System.out.println("Base64 encoded Protobuf products for use in kafka-protobuf-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); + System.out.println("Base64 encoded Protobuf products with different message index scenarios:"); + System.out.println("\n1. Standard Protobuf (no message index):"); + System.out.println("value: \"" + standardProduct + "\""); + + System.out.println("\n2. Simple Message Index (single 0):"); + System.out.println("value: \"" + productWithSimpleIndex + "\""); + + System.out.println("\n3. Complex Message Index (array [1,0]):"); + System.out.println("value: \"" + productWithComplexIndex + "\""); + + // Print the merged event structure + System.out.println("\n" + "=".repeat(80)); + System.out.println("MERGED EVENT WITH ALL THREE SCENARIOS"); + System.out.println("=".repeat(80)); + printSampleEvent(encodedKey, standardProduct, productWithSimpleIndex, productWithComplexIndex); } private static String serializeAndEncode(ProtobufProduct product) { return Base64.getEncoder().encodeToString(product.toByteArray()); } + /** + * Serializes a protobuf product with a simple Confluent message index (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 serializeWithSimpleMessageIndex(ProtobufProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); + + // Write simple message index (single 0) + codedOutput.writeUInt32NoTag(0); + + // Write the protobuf data + product.writeTo(codedOutput); + + codedOutput.flush(); + 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 + * + * @see {@link https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format} + */ + private static String serializeWithComplexMessageIndex(ProtobufProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); + + // Write complex message index array [1,0] + codedOutput.writeUInt32NoTag(2); // Array length + codedOutput.writeUInt32NoTag(1); // First index value + codedOutput.writeUInt32NoTag(0); // Second index value + + // 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 product1, String product2, String product3) { + private static void printSampleEvent(String key, String standardProduct, String simpleIndexProduct, + String complexIndexProduct) { 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" @@ -83,7 +120,7 @@ private static void printSampleEvent(String key, String product1, String product " \"timestamp\": 1545084650987,\n" + " \"timestampType\": \"CREATE_TIME\",\n" + " \"key\": \"" + key + "\",\n" + - " \"value\": \"" + product1 + "\",\n" + + " \"value\": \"" + standardProduct + "\",\n" + " \"headers\": [\n" + " {\n" + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + @@ -97,7 +134,7 @@ private static void printSampleEvent(String key, String product1, String product " \"timestamp\": 1545084650988,\n" + " \"timestampType\": \"CREATE_TIME\",\n" + " \"key\": \"" + key + "\",\n" + - " \"value\": \"" + product2 + "\",\n" + + " \"value\": \"" + simpleIndexProduct + "\",\n" + " \"headers\": [\n" + " {\n" + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + @@ -111,7 +148,7 @@ private static void printSampleEvent(String key, String product1, String product " \"timestamp\": 1545084650989,\n" + " \"timestampType\": \"CREATE_TIME\",\n" + " \"key\": null,\n" + - " \"value\": \"" + product3 + "\",\n" + + " \"value\": \"" + complexIndexProduct + "\",\n" + " \"headers\": [\n" + " {\n" + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + 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 index 025f203c4..c15be552f 100644 --- 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 @@ -13,14 +13,27 @@ package software.amazon.lambda.powertools.kafka.serializers; import java.io.IOException; + +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 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); + @Override @SuppressWarnings("unchecked") protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException { @@ -29,7 +42,9 @@ protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException try { // Get the parser from the generated Protobuf class Parser<Message> parser = (Parser<Message>) type.getMethod("parser").invoke(null); - Message message = parser.parseFrom(data); + + // Try to deserialize the data, handling potential Confluent message indices + Message message = deserializeWithMessageIndexHandling(data, parser); return type.cast(message); } catch (Exception e) { throw new IOException("Failed to deserialize Protobuf data.", e); @@ -40,4 +55,45 @@ protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException + "Consider using an alternative Deserializer."); } } + + private Message deserializeWithMessageIndexHandling(byte[] data, Parser<Message> parser) throws IOException { + try { + LOGGER.debug("Attempting to deserialize as standard protobuf data"); + return parser.parseFrom(data); + } catch (Exception e) { + LOGGER.debug("Standard protobuf parsing failed, attempting Confluent message-index handling"); + return deserializeWithMessageIndex(data, parser); + } + } + + private Message deserializeWithMessageIndex(byte[] data, Parser<Message> parser) throws IOException { + CodedInputStream codedInputStream = CodedInputStream.newInstance(data); + + try { + // https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format + // Read the first varint - this could be: + // 1. A single 0 (simple case - first message type) + // 2. The length of the message index array (complex case) + int firstValue = codedInputStream.readUInt32(); + + if (firstValue == 0) { + // Simple case: Single 0 byte means first message type + LOGGER.debug("Found simple message-index case (single 0), parsing remaining data as protobuf"); + return parser.parseFrom(codedInputStream); + } else { + // Complex case: firstValue is the length of the message index array + LOGGER.debug("Found complex message-index case with array length: {}, skipping {} message index values", + firstValue, firstValue); + for (int i = 0; i < firstValue; i++) { + codedInputStream.readUInt32(); // Skip each message index value + } + // Now the remaining data should be the actual protobuf message + LOGGER.debug("Finished skipping message indexes, parsing remaining data as protobuf"); + return parser.parseFrom(codedInputStream); + } + + } catch (Exception e) { + throw new IOException("Failed to parse protobuf data with or without message index", e); + } + } } 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 index 2d506de4b..3315e1172 100644 --- 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 @@ -15,11 +15,14 @@ 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 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 { @@ -72,4 +75,78 @@ void shouldThrowExceptionWhenDeserializingInvalidProtobufData() { .isInstanceOf(IOException.class) .hasMessageContaining("Failed to deserialize Protobuf data"); } + + @Test + void shouldDeserializeProtobufDataWithSimpleMessageIndex() 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 = createProtobufDataWithSimpleMessageIndex(product); + + // When + TestProduct result = deserializer.deserializeObject(protobufDataWithSimpleIndex, TestProduct.class); + + // 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 [1,0]) + byte[] protobufDataWithComplexIndex = createProtobufDataWithComplexMessageIndex(product); + + // When + TestProduct result = deserializer.deserializeObject(protobufDataWithComplexIndex, TestProduct.class); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(789); + assertThat(result.getName()).isEqualTo("Complex Index Product"); + assertThat(result.getPrice()).isEqualTo(299.99); + } + + private byte[] createProtobufDataWithSimpleMessageIndex(TestProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); + + // Write simple message index (single 0) + codedOutput.writeUInt32NoTag(0); + + // Write the protobuf data + product.writeTo(codedOutput); + + codedOutput.flush(); + return baos.toByteArray(); + } + + private byte[] createProtobufDataWithComplexMessageIndex(TestProduct product) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); + + // Write complex message index array [1,0] + codedOutput.writeUInt32NoTag(2); // Array length + codedOutput.writeUInt32NoTag(1); // First index value + codedOutput.writeUInt32NoTag(0); // Second index value + + // Write the protobuf data + product.writeTo(codedOutput); + + codedOutput.flush(); + return baos.toByteArray(); + } } From cf01e91eb0b398b89b8fb442edfe27e1532a5810 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 19 Jun 2025 15:31:48 +0200 Subject: [PATCH 0685/1008] fix(ci): Add maven project description to Kafka utility. (#1903) --- powertools-kafka/pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index f5b80012c..f96c1eb82 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -28,7 +28,10 @@ <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Kafka Consumer</name> - <description></description> + <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.0.0</kafka-clients.version> From 8f70e83b28fc939f884d4cea150dfb3f3ee3c75e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:52:21 +0200 Subject: [PATCH 0686/1008] chore(ci): bump version to 2.1.0 (#1904) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 42 files changed, 48 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 88955bca7..165458a65 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index ea1e8d542..496c933c3 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index ef3c0e4e0..1cf1e6f65 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 43c81b9f8..74a0090fe 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index fa5a1927a..0c4dec217 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index b869a5672..2ddd389a9 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.162.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 4cf988a6f..37d9f4554 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.11.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.0.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.0.0' - aspect 'software.amazon.lambda:powertools-metrics:2.0.0' + aspect 'software.amazon.lambda:powertools-tracing:2.1.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.1.0' + aspect 'software.amazon.lambda:powertools-metrics:2.1.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 7029dc458..de820300d 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.11.3") 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.0.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.0.0") - aspect("software.amazon.lambda:powertools-metrics:2.0.0") + aspect("software.amazon.lambda:powertools-tracing:2.1.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.1.0") + aspect("software.amazon.lambda:powertools-metrics:2.1.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index b2353b86e..ead4625f5 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 813fc267f..44f171698 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 1aea70820..42d70ba76 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index dd4c385c0..a9ab410e3 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index c7ceabc57..06a50c16b 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index a745ac75d..185790b64 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index f2ce0f21f..320ed42cd 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index a797bbeed..ea8029b8c 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index b96d02b6b..f9408012c 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 9b6df9783..3b9d9baeb 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.0.0</version> + <version>2.1.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 07be3c175..1880e543d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,7 +121,7 @@ extra_javascript: extra: powertools: - version: 2.0.0 + version: 2.1.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index f27ffc497..0542295bb 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 59e5b8c93..4f6b78f5e 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 271704dea..657d42e42 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index dd6ef8e61..8e0229bb4 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 4f1c059e9..c49e6824a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 862cf3160..169953642 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 58d184fc5..846dea2d8 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index b92d66dbc..099eb12ab 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index f96c1eb82..deac5cc3e 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index b23e3f41c..8eb0e143f 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 75d5853e5..2ee71ae67 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 75aa94a97..6fe11ea93 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 8b2a5cfd5..6fd411c47 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 460eb220f..19b185f4d 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 96f6f50b3..d61e76322 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index a3822d11b..52b15296b 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 9c7030d7c..0b82087c9 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 99a308825..c90cf1c69 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 46cf939ba..f03983fff 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 4d2b5d145..9f8b12911 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.0.0</version> + <version>2.1.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 986a3b1d9..d6e04b3b8 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 14d6d51b1..d76ddffcb 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index bfedf8d40..a7403d6b3 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.0.0</version> + <version>2.1.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 4a8511d95a2a9227f0d57ef74803202bb950491b Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 20 Jun 2025 13:29:08 +0200 Subject: [PATCH 0687/1008] fix(kafka): Handle message indices in proto data also for Glue Schema Registry (#1907) * Bug fix to handle message indices in proto data * Removing links and changing log level from info to debug * Upgrade lambda.events.version tp 3.16.0 * Update Glue schema id to 36. Add some tests to increase coverage. Use Kafka ByteUtils also in unit tests. * Update lambda java events to 3.16.0 everywhere. * Add sample generation code for all Protobuf cases. --------- Co-authored-by: Karthik Puttaswamy <karthikx@amazon.com> --- .../cdk/app/pom.xml | 2 +- .../gradle/build.gradle | 2 +- .../kotlin/build.gradle.kts | 4 +- .../sam-graalvm/pom.xml | 2 +- .../sam/pom.xml | 2 +- .../serverless/pom.xml | 2 +- .../terraform/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- .../events/kafka-protobuf-event.json | 32 ++++- .../powertools-examples-kafka/tools/pom.xml | 6 + .../kafka/tools/GenerateProtobufSamples.java | 115 +++++++++++++----- .../sam-graalvm/pom.xml | 2 +- .../sam/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- pom.xml | 2 +- .../AbstractKafkaDeserializer.java | 60 +++++++-- .../serializers/KafkaAvroDeserializer.java | 3 +- .../serializers/KafkaJsonDeserializer.java | 3 +- .../KafkaProtobufDeserializer.java | 93 +++++++------- .../AbstractKafkaDeserializerTest.java | 111 ++++++++++++++++- .../KafkaAvroDeserializerTest.java | 17 +-- .../KafkaJsonDeserializerTest.java | 8 +- .../KafkaProtobufDeserializerTest.java | 96 +++++++++++---- 23 files changed, 433 insertions(+), 137 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 0c4dec217..87bea4b38 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 37d9f4554..b86e49659 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -26,7 +26,7 @@ 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.11.0' + 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.1.0' diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index de820300d..23c84a0d3 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -12,7 +12,7 @@ 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.11.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.1.0") @@ -23,4 +23,4 @@ dependencies { kotlin { jvmToolchain(11) -} \ No newline at end of file +} diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index ead4625f5..d691c02a5 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.11.3</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 44f171698..f37319ea1 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 42d70ba76..1f4e0a2fc 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index a9ab410e3..6dcd2f8c6 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 06a50c16b..c7ca6a058 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -52,7 +52,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-kafka/events/kafka-protobuf-event.json b/examples/powertools-examples-kafka/events/kafka-protobuf-event.json index e0547ad88..6f5ec58ae 100644 --- a/examples/powertools-examples-kafka/events/kafka-protobuf-event.json +++ b/examples/powertools-examples-kafka/events/kafka-protobuf-event.json @@ -30,7 +30,11 @@ { "headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101] } - ] + ], + "valueSchemaMetadata": { + "schemaId": "123", + "dataFormat": "PROTOBUF" + } }, { "topic": "mytopic", @@ -39,12 +43,34 @@ "timestamp": 1545084650989, "timestampType": "CREATE_TIME", "key": null, - "value": "AgEACOkHEgZMYXB0b3AZUrgehes/j0A=", + "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/tools/pom.xml b/examples/powertools-examples-kafka/tools/pom.xml index 97231e5bd..80ed6c264 100644 --- a/examples/powertools-examples-kafka/tools/pom.xml +++ b/examples/powertools-examples-kafka/tools/pom.xml @@ -13,6 +13,7 @@ <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> @@ -26,6 +27,11 @@ <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> 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 index eecd3e1cc..aa5f6e330 100644 --- 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 @@ -2,8 +2,10 @@ 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; @@ -19,37 +21,42 @@ private GenerateProtobufSamples() { } public static void main(String[] args) throws IOException { - // Create a single product that will be used for all three scenarios + // 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 three different serializations of the same product + // Create four different serializations of the same product String standardProduct = serializeAndEncode(product); - String productWithSimpleIndex = serializeWithSimpleMessageIndex(product); - String productWithComplexIndex = serializeWithComplexMessageIndex(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 message index scenarios:"); - System.out.println("\n1. Standard Protobuf (no message index):"); + 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. Simple Message Index (single 0):"); - System.out.println("value: \"" + productWithSimpleIndex + "\""); + System.out.println("\n2. Confluent with Simple Message Index (optimized single 0):"); + System.out.println("value: \"" + productWithConfluentSimpleIndex + "\""); - System.out.println("\n3. Complex Message Index (array [1,0]):"); - System.out.println("value: \"" + productWithComplexIndex + "\""); + 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 THREE SCENARIOS"); + System.out.println("MERGED EVENT WITH ALL FOUR SCENARIOS"); System.out.println("=".repeat(80)); - printSampleEvent(encodedKey, standardProduct, productWithSimpleIndex, productWithComplexIndex); + printSampleEvent(encodedKey, standardProduct, productWithConfluentSimpleIndex, productWithConfluentComplexIndex, + productWithGlueMagicByte); } private static String serializeAndEncode(ProtobufProduct product) { @@ -57,39 +64,59 @@ private static String serializeAndEncode(ProtobufProduct product) { } /** - * Serializes a protobuf product with a simple Confluent message index (single 0). + * 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 serializeWithSimpleMessageIndex(ProtobufProduct product) throws IOException { + private static String serializeWithConfluentSimpleMessageIndex(ProtobufProduct product) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); - // Write simple message index (single 0) - codedOutput.writeUInt32NoTag(0); + // Write optimized simple message index for Confluent (single 0 byte for [0]) + baos.write(0); // Write the protobuf data - product.writeTo(codedOutput); + baos.write(product.toByteArray()); - codedOutput.flush(); 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 + * 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 serializeWithComplexMessageIndex(ProtobufProduct product) throws IOException { + 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 complex message index array [1,0] - codedOutput.writeUInt32NoTag(2); // Array length - codedOutput.writeUInt32NoTag(1); // First index value - codedOutput.writeUInt32NoTag(0); // Second index value + // Write Glue magic byte (single UInt32) + codedOutput.writeUInt32NoTag(1); // Write the protobuf data product.writeTo(codedOutput); @@ -103,8 +130,8 @@ private static String serializeAndEncodeInteger(Integer value) { return Base64.getEncoder().encodeToString(value.toString().getBytes()); } - private static void printSampleEvent(String key, String standardProduct, String simpleIndexProduct, - String complexIndexProduct) { + 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" @@ -134,12 +161,16 @@ private static void printSampleEvent(String key, String standardProduct, String " \"timestamp\": 1545084650988,\n" + " \"timestampType\": \"CREATE_TIME\",\n" + " \"key\": \"" + key + "\",\n" + - " \"value\": \"" + simpleIndexProduct + "\",\n" + + " \"value\": \"" + confluentSimpleProduct + "\",\n" + " \"headers\": [\n" + " {\n" + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + " }\n" + - " ]\n" + + " ],\n" + + " \"valueSchemaMetadata\": {\n" + + " \"schemaId\": \"123\",\n" + + " \"dataFormat\": \"PROTOBUF\"\n" + + " }\n" + " },\n" + " {\n" + " \"topic\": \"mytopic\",\n" + @@ -148,12 +179,34 @@ private static void printSampleEvent(String key, String standardProduct, String " \"timestamp\": 1545084650989,\n" + " \"timestampType\": \"CREATE_TIME\",\n" + " \"key\": null,\n" + - " \"value\": \"" + complexIndexProduct + "\",\n" + + " \"value\": \"" + confluentComplexProduct + "\",\n" + " \"headers\": [\n" + " {\n" + " \"headerKey\": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]\n" + " }\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/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 320ed42cd..643b8f134 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index ea8029b8c..e8ce66e11 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index f9408012c..eedce8b87 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.15.0</version> + <version>3.16.0</version> </dependency> </dependencies> diff --git a/pom.xml b/pom.xml index 0542295bb..1033c3f6e 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.2.3</lambda.core.version> - <lambda.events.version>3.15.0</lambda.events.version> + <lambda.events.version>3.16.0</lambda.events.version> <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> 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 index 8d0fc8f61..3de8320a5 100644 --- 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 @@ -41,6 +41,11 @@ 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 @@ -170,8 +175,8 @@ private <K, V> ConsumerRecord<K, V> convertToConsumerRecord( Class<K> keyType, Class<V> valueType) { - K key = deserializeField(eventRecord.getKey(), keyType, "key"); - V value = deserializeField(eventRecord.getValue(), valueType, "value"); + 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<>( @@ -190,14 +195,15 @@ private <K, V> ConsumerRecord<K, V> convertToConsumerRecord( Optional.empty()); } - private <T> T deserializeField(String encodedData, Class<T> type, String fieldName) { + 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); + return deserialize(decodedBytes, type, schemaRegistryType); } catch (Exception e) { throw new RuntimeException("Failed to deserialize Kafka record " + fieldName + ".", e); } @@ -218,28 +224,60 @@ private Headers extractHeaders(KafkaEvent.KafkaEventRecord eventRecord) { 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). - * + * 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) throws IOException; + 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. - * + * 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) throws IOException { + 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) { @@ -247,7 +285,7 @@ private <T> T deserialize(byte[] data, Class<T> type) throws IOException { } // Delegate to subclass for complex type deserialization - return deserializeObject(data, type); + return deserializeObject(data, type, schemaRegistryType); } /** 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 index ddf09d4ff..70e4affac 100644 --- 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 @@ -26,7 +26,8 @@ public class KafkaAvroDeserializer extends AbstractKafkaDeserializer { @Override - protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException { + 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 { 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 index ed64f3786..733fe5d7d 100644 --- 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 @@ -21,7 +21,8 @@ public class KafkaJsonDeserializer extends AbstractKafkaDeserializer { @Override - protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException { + 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 index c15be552f..1a8085a61 100644 --- 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 @@ -13,7 +13,10 @@ 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; @@ -23,7 +26,8 @@ /** * Deserializer for Kafka records using Protocol Buffers format. - * Supports both standard protobuf serialization and Confluent Schema Registry serialization using messages indices. + * 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. @@ -33,19 +37,22 @@ public class KafkaProtobufDeserializer extends AbstractKafkaDeserializer { private static final Logger LOGGER = LoggerFactory.getLogger(KafkaProtobufDeserializer.class); + private static final String PROTOBUF_PARSER_METHOD = "parser"; @Override - @SuppressWarnings("unchecked") - protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException { - // If no Protobuf generated class is passed we cannot deserialize using Protobuf + 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 { - // Get the parser from the generated Protobuf class - Parser<Message> parser = (Parser<Message>) type.getMethod("parser").invoke(null); - - // Try to deserialize the data, handling potential Confluent message indices - Message message = deserializeWithMessageIndexHandling(data, parser); - return type.cast(message); + 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); } @@ -56,44 +63,48 @@ protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException } } - private Message deserializeWithMessageIndexHandling(byte[] data, Parser<Message> parser) throws IOException { + @SuppressWarnings("unchecked") + private <T> T defaultDeserializer(byte[] data, Class<T> type) throws IOException { try { - LOGGER.debug("Attempting to deserialize as standard protobuf data"); - return parser.parseFrom(data); + 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) { - LOGGER.debug("Standard protobuf parsing failed, attempting Confluent message-index handling"); - return deserializeWithMessageIndex(data, parser); + throw new IOException("Failed to deserialize Protobuf data.", e); } } - private Message deserializeWithMessageIndex(byte[] data, Parser<Message> parser) throws IOException { - CodedInputStream codedInputStream = CodedInputStream.newInstance(data); - - try { - // https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format - // Read the first varint - this could be: - // 1. A single 0 (simple case - first message type) - // 2. The length of the message index array (complex case) - int firstValue = codedInputStream.readUInt32(); + @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); - if (firstValue == 0) { - // Simple case: Single 0 byte means first message type - LOGGER.debug("Found simple message-index case (single 0), parsing remaining data as protobuf"); - return parser.parseFrom(codedInputStream); - } else { - // Complex case: firstValue is the length of the message index array - LOGGER.debug("Found complex message-index case with array length: {}, skipping {} message index values", - firstValue, firstValue); - for (int i = 0; i < firstValue; i++) { - codedInputStream.readUInt32(); // Skip each message index value - } - // Now the remaining data should be the actual protobuf message - LOGGER.debug("Finished skipping message indexes, parsing remaining data as protobuf"); - return parser.parseFrom(codedInputStream); + // 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); } - - } catch (Exception e) { - throw new IOException("Failed to parse protobuf data with or without message index", e); } + 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/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/serializers/AbstractKafkaDeserializerTest.java index 512058bca..b40a2a2b0 100644 --- 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 @@ -459,10 +459,117 @@ void shouldThrowExceptionWhenConvertingEmptyStringToChar(InputType inputType) { } } + @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 class TestDeserializer extends AbstractKafkaDeserializer { + private static final class TestDeserializer extends AbstractKafkaDeserializer { @Override - protected <T> T deserializeObject(byte[] data, Class<T> type) throws IOException { + protected <T> T deserializeObject(byte[] data, Class<T> type, SchemaRegistryType schemaRegistryType) + throws IOException { return objectMapper.readValue(data, type); } } 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 index a0b59b136..f501d7d98 100644 --- 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 @@ -38,9 +38,10 @@ void shouldThrowExceptionWhenTypeIsNotAvroSpecificRecord() { byte[] data = new byte[] { 1, 2, 3 }; // When/Then - assertThatThrownBy(() -> deserializer.deserializeObject(data, String.class)) - .isInstanceOf(IOException.class) - .hasMessageContaining("Unsupported type for Avro deserialization"); + assertThatThrownBy(() -> deserializer.deserializeObject(data, String.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Unsupported type for Avro deserialization"); } @Test @@ -50,7 +51,8 @@ void shouldDeserializeValidAvroData() throws IOException { byte[] avroData = serializeAvro(product); // When - TestProduct result = deserializer.deserializeObject(avroData, TestProduct.class); + TestProduct result = deserializer.deserializeObject(avroData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE); // Then assertThat(result).isNotNull(); @@ -65,9 +67,10 @@ void shouldThrowExceptionWhenDeserializingInvalidAvroData() { byte[] invalidAvroData = new byte[] { 1, 2, 3, 4, 5 }; // When/Then - assertThatThrownBy(() -> deserializer.deserializeObject(invalidAvroData, TestProduct.class)) - .isInstanceOf(IOException.class) - .hasMessageContaining("Failed to deserialize Avro data"); + 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 index 0cfb2498b..c01e09d8d 100644 --- 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 @@ -42,8 +42,9 @@ void shouldThrowExceptionWhenTypeIsNotSupportedForJson() { byte[] data = new byte[] { 1, 2, 3 }; // When/Then - assertThatThrownBy(() -> deserializer.deserializeObject(data, Object.class)) - .isInstanceOf(JsonParseException.class); + assertThatThrownBy(() -> deserializer.deserializeObject(data, Object.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(JsonParseException.class); } @Test @@ -53,7 +54,8 @@ void shouldDeserializeValidJsonData() throws IOException { byte[] jsonData = objectMapper.writeValueAsBytes(product); // When - TestProductPojo result = deserializer.deserializeObject(jsonData, TestProductPojo.class); + TestProductPojo result = deserializer.deserializeObject(jsonData, TestProductPojo.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE); // Then assertThat(result).isNotNull(); 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 index 3315e1172..34a376947 100644 --- 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 @@ -17,7 +17,9 @@ 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; @@ -40,9 +42,10 @@ void shouldThrowExceptionWhenTypeIsNotProtobufMessage() { byte[] data = new byte[] { 1, 2, 3 }; // When/Then - assertThatThrownBy(() -> deserializer.deserializeObject(data, String.class)) - .isInstanceOf(IOException.class) - .hasMessageContaining("Unsupported type for Protobuf deserialization"); + assertThatThrownBy(() -> deserializer.deserializeObject(data, String.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Unsupported type for Protobuf deserialization"); } @Test @@ -56,7 +59,8 @@ void shouldDeserializeValidProtobufData() throws IOException { byte[] protobufData = product.toByteArray(); // When - TestProduct result = deserializer.deserializeObject(protobufData, TestProduct.class); + TestProduct result = deserializer.deserializeObject(protobufData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE); // Then assertThat(result).isNotNull(); @@ -71,13 +75,14 @@ void shouldThrowExceptionWhenDeserializingInvalidProtobufData() { byte[] invalidProtobufData = new byte[] { 1, 2, 3, 4, 5 }; // When/Then - assertThatThrownBy(() -> deserializer.deserializeObject(invalidProtobufData, TestProduct.class)) - .isInstanceOf(IOException.class) - .hasMessageContaining("Failed to deserialize Protobuf data"); + assertThatThrownBy(() -> deserializer.deserializeObject(invalidProtobufData, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.NONE)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Failed to deserialize Protobuf data"); } @Test - void shouldDeserializeProtobufDataWithSimpleMessageIndex() throws IOException { + void shouldDeserializeProtobufDataWithSimpleMessageIndexGlue() throws IOException { // Given TestProduct product = TestProduct.newBuilder() .setId(456) @@ -86,10 +91,11 @@ void shouldDeserializeProtobufDataWithSimpleMessageIndex() throws IOException { .build(); // Create protobuf data with simple message index (single 0) - byte[] protobufDataWithSimpleIndex = createProtobufDataWithSimpleMessageIndex(product); + byte[] protobufDataWithSimpleIndex = createProtobufDataWithGlueMagicByte(product); // When - TestProduct result = deserializer.deserializeObject(protobufDataWithSimpleIndex, TestProduct.class); + TestProduct result = deserializer.deserializeObject(protobufDataWithSimpleIndex, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.GLUE); // Then assertThat(result).isNotNull(); @@ -107,11 +113,12 @@ void shouldDeserializeProtobufDataWithComplexMessageIndex() throws IOException { .setPrice(299.99) .build(); - // Create protobuf data with complex message index (array [1,0]) - byte[] protobufDataWithComplexIndex = createProtobufDataWithComplexMessageIndex(product); + // Create protobuf data with complex message index (array [2,2]) + byte[] protobufDataWithComplexIndex = createProtobufDataWithComplexMessageIndexConfluent(product); // When - TestProduct result = deserializer.deserializeObject(protobufDataWithComplexIndex, TestProduct.class); + TestProduct result = deserializer.deserializeObject(protobufDataWithComplexIndex, TestProduct.class, + AbstractKafkaDeserializer.SchemaRegistryType.CONFLUENT); // Then assertThat(result).isNotNull(); @@ -120,12 +127,35 @@ void shouldDeserializeProtobufDataWithComplexMessageIndex() throws IOException { assertThat(result.getPrice()).isEqualTo(299.99); } - private byte[] createProtobufDataWithSimpleMessageIndex(TestProduct product) throws IOException { + @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 (single 0) - codedOutput.writeUInt32NoTag(0); + // Write simple message index for Glue (single UInt32) + codedOutput.writeUInt32NoTag(1); // Write the protobuf data product.writeTo(codedOutput); @@ -134,19 +164,37 @@ private byte[] createProtobufDataWithSimpleMessageIndex(TestProduct product) thr return baos.toByteArray(); } - private byte[] createProtobufDataWithComplexMessageIndex(TestProduct product) throws IOException { + private byte[] createProtobufDataWithSimpleMessageIndexConfluent(TestProduct product) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - CodedOutputStream codedOutput = CodedOutputStream.newInstance(baos); - // Write complex message index array [1,0] - codedOutput.writeUInt32NoTag(2); // Array length - codedOutput.writeUInt32NoTag(1); // First index value - codedOutput.writeUInt32NoTag(0); // Second index value + // 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 - product.writeTo(codedOutput); + 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()); - codedOutput.flush(); return baos.toByteArray(); } } From 984036ca2e3afb92b2c838bcfa2e48a51c3d7a64 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:53:09 +0200 Subject: [PATCH 0688/1008] chore(ci): bump version to 2.1.1 (#1910) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 42 files changed, 48 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 165458a65..7e33bf19e 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 496c933c3..cd076d099 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 1cf1e6f65..f1ae54c46 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 74a0090fe..ea31c3caa 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 87bea4b38..e9f2318a9 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 2ddd389a9..4af44d146 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.162.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index b86e49659..04368e29c 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.1.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.1.0' - aspect 'software.amazon.lambda:powertools-metrics:2.1.0' + aspect 'software.amazon.lambda:powertools-tracing:2.1.1' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.1.1' + aspect 'software.amazon.lambda:powertools-metrics:2.1.1' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 23c84a0d3..2b3d184d7 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.1.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.1.0") - aspect("software.amazon.lambda:powertools-metrics:2.1.0") + aspect("software.amazon.lambda:powertools-tracing:2.1.1") + aspect("software.amazon.lambda:powertools-logging-log4j:2.1.1") + aspect("software.amazon.lambda:powertools-metrics:2.1.1") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index d691c02a5..25517be25 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index f37319ea1..db4fbc2b3 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 1f4e0a2fc..b9b9304b3 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 6dcd2f8c6..82be84f06 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index c7ca6a058..b5864de27 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 185790b64..6921f2793 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 643b8f134..06dba9c56 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index e8ce66e11..6c152479e 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index eedce8b87..35d0f76d1 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ 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.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-serialization</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 3b9d9baeb..9beb3d014 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.1.0</version> + <version>2.1.1</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 1880e543d..0008ef9dd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,7 +121,7 @@ extra_javascript: extra: powertools: - version: 2.1.0 + version: 2.1.1 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 1033c3f6e..407fa518b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 4f6b78f5e..c5036e74a 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 657d42e42..1f3329325 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 8e0229bb4..20060bae7 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index c49e6824a..779476eae 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 169953642..029fa4163 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 846dea2d8..c8ea35d4b 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 099eb12ab..4ae37f00c 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index deac5cc3e..5eb79913b 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 8eb0e143f..1b975739d 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 2ee71ae67..af9f066b3 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 6fe11ea93..2cfcd967d 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 6fd411c47..cb2054b87 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 19b185f4d..3501c7b0e 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index d61e76322..d4da33a8d 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 52b15296b..206d259e1 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 0b82087c9..3959ff65b 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index c90cf1c69..9885fcef1 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index f03983fff..e9f33a24b 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 9f8b12911..21ce5639e 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.0</version> + <version>2.1.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index d6e04b3b8..891a1582a 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d76ddffcb..5239e7b6c 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index a7403d6b3..3ab6e51fc 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.0</version> + <version>2.1.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 8b6d484c37b44359bcb534083e2506107c385fd9 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:46:59 +0100 Subject: [PATCH 0689/1008] chore(ci): Update branch protection rules (#1914) --- .github/branch_protection_settings/1.x.x.json | 53 +++++++++++++++++++ .github/branch_protection_settings/main.json | 42 +++++++++------ 2 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 .github/branch_protection_settings/1.x.x.json diff --git a/.github/branch_protection_settings/1.x.x.json b/.github/branch_protection_settings/1.x.x.json new file mode 100644 index 000000000..e52aba745 --- /dev/null +++ b/.github/branch_protection_settings/1.x.x.json @@ -0,0 +1,53 @@ +{ + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection", + "required_status_checks": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_status_checks", + "strict": true, + "contexts": [ + "SonarCloud" + ], + "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_status_checks/contexts", + "checks": [ + { + "context": "SonarCloud", + "app_id": null + } + ] + }, + "required_pull_request_reviews": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_pull_request_reviews", + "dismiss_stale_reviews": false, + "require_code_owner_reviews": false, + "require_last_push_approval": false, + "required_approving_review_count": 0 + }, + "required_signatures": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_signatures", + "enabled": false + }, + "enforce_admins": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/enforce_admins", + "enabled": true + }, + "required_linear_history": { + "enabled": false + }, + "allow_force_pushes": { + "enabled": false + }, + "allow_deletions": { + "enabled": false + }, + "block_creations": { + "enabled": false + }, + "required_conversation_resolution": { + "enabled": false + }, + "lock_branch": { + "enabled": false + }, + "allow_fork_syncing": { + "enabled": false + } +} diff --git a/.github/branch_protection_settings/main.json b/.github/branch_protection_settings/main.json index d283b3d5f..9fc6cc07d 100644 --- a/.github/branch_protection_settings/main.json +++ b/.github/branch_protection_settings/main.json @@ -3,23 +3,33 @@ "required_status_checks": { "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks", "strict": true, - "contexts": [ - "SonarCloud" - ], + "contexts": [], "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks/contexts", - "checks": [ - { - "context": "SonarCloud", - "app_id": 57789 - } - ] + "checks": [] + }, + "restrictions": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions", + "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/users", + "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/teams", + "apps_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/apps", + "users": [], + "teams": [], + "apps": [] }, "required_pull_request_reviews": { "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_pull_request_reviews", - "dismiss_stale_reviews": false, + "dismiss_stale_reviews": true, "require_code_owner_reviews": false, - "require_last_push_approval": false, - "required_approving_review_count": 0 + "require_last_push_approval": true, + "required_approving_review_count": 1, + "dismissal_restrictions": { + "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions", + "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions/users", + "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions/teams", + "users": [], + "teams": [], + "apps": [] + } }, "required_signatures": { "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_signatures", @@ -27,10 +37,10 @@ }, "enforce_admins": { "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/enforce_admins", - "enabled": true + "enabled": false }, "required_linear_history": { - "enabled": false + "enabled": true }, "allow_force_pushes": { "enabled": false @@ -39,10 +49,10 @@ "enabled": false }, "block_creations": { - "enabled": false + "enabled": true }, "required_conversation_resolution": { - "enabled": false + "enabled": true }, "lock_branch": { "enabled": false From ffd88b96a6d57ea350e5ee3af4c980ef6ccffc7e Mon Sep 17 00:00:00 2001 From: Andrea Amorosi <dreamorosi@gmail.com> Date: Mon, 7 Jul 2025 12:37:44 +0200 Subject: [PATCH 0690/1008] chore: update issue, PR, and discussion templates (#1915) * chore: update issue & discussion templates * chore: update PR template * Update .github/PULL_REQUEST_TEMPLATE.md * Update .github/DISCUSSION_TEMPLATE/rfcs.yml * Update .github/DISCUSSION_TEMPLATE/rfcs.yml --------- Co-authored-by: Philipp Page <github@philipp.page> --- .../rfc.md => DISCUSSION_TEMPLATE/rfcs.yml} | 32 ++++++------ .../{bug_report.md => bug_report.yml} | 3 +- .github/ISSUE_TEMPLATE/config.yml | 5 +- .github/ISSUE_TEMPLATE/feature_request.md | 20 ------- .github/ISSUE_TEMPLATE/feature_request.yml | 52 +++++++++++++++++++ .github/ISSUE_TEMPLATE/share_your_work.yml | 2 +- .github/ISSUE_TEMPLATE/support_powertools.yml | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 33 ++++++------ 8 files changed, 94 insertions(+), 55 deletions(-) rename .github/{ISSUE_TEMPLATE/rfc.md => DISCUSSION_TEMPLATE/rfcs.yml} (78%) rename .github/ISSUE_TEMPLATE/{bug_report.md => bug_report.yml} (98%) delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/DISCUSSION_TEMPLATE/rfcs.yml similarity index 78% rename from .github/ISSUE_TEMPLATE/rfc.md rename to .github/DISCUSSION_TEMPLATE/rfcs.yml index ae2337402..55909514f 100644 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ b/.github/DISCUSSION_TEMPLATE/rfcs.yml @@ -1,7 +1,4 @@ -name: Request for Comments (RFC) -description: Feature design and detailed proposals -title: "RFC: TITLE" -labels: ["RFC", "triage"] +title: "RFC: <Title>" body: - type: markdown attributes: @@ -15,21 +12,18 @@ body: - type: dropdown id: area attributes: - label: Which Powertools for AWS Lambda (Java) utility does this relate to? + label: Which area does this RFC relate to? options: - Tracer - Logger - Metrics - - Middleware factory - Parameters - - Batch processing - - Typing + - Large Messages + - Batch Processing - Validation - - Event Source Data Classes - - Parser - Idempotency - - Feature flags - - JMESPath functions + - Custom Resources + - Serialization - Other validations: required: true @@ -81,7 +75,7 @@ body: attributes: label: Alternative solutions description: Please describe what alternative solutions to this use case, if any - render: markdown + render: Markdown validations: required: false - type: checkboxes @@ -89,7 +83,7 @@ body: attributes: label: Acknowledgment options: - - label: This feature request meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda/Java/#tenets) + - 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 @@ -98,10 +92,16 @@ body: value: | --- - **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. + **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: '' \ No newline at end of file + * 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.yml similarity index 98% rename from .github/ISSUE_TEMPLATE/bug_report.md rename to .github/ISSUE_TEMPLATE/bug_report.yml index 8810605b9..2b0ae71e8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,7 @@ name: Bug report description: Report a reproducible bug to help us improve title: "Bug: TITLE" +type: "Bug" labels: ["bug", "triage"] body: - type: markdown @@ -49,7 +50,7 @@ body: id: version attributes: label: Powertools for AWS Lambda (Java) version - placeholder: "latest, 1.19.0" + placeholder: "latest, 2.0.1" value: latest validations: required: true 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/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml index 01dae4fdf..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: diff --git a/.github/ISSUE_TEMPLATE/support_powertools.yml b/.github/ISSUE_TEMPLATE/support_powertools.yml index 8623c2b73..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: 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 From 64ddf340e814f276dcc0056c25b1756a9d83f2de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:53:21 +0200 Subject: [PATCH 0691/1008] build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.12 to 0.8.13 (#1849) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.12 to 0.8.13. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.12...v0.8.13) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 407fa518b..4d4c15db2 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ <junit.version>5.10.0</junit.version> <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> - <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version> + <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> From f73dee8e2638719bea7a1cc2b96a7521a7cc5c70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:02:53 +0200 Subject: [PATCH 0692/1008] build(deps): bump aws.sdk.version from 2.31.40 to 2.31.62 (#1886) Bumps `aws.sdk.version` from 2.31.40 to 2.31.62. Updates `software.amazon.awssdk:bom` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:http-client-spi` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:url-connection-client` from 2.30.31 to 2.31.62 Updates `software.amazon.awssdk:dynamodb` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:s3` from 2.30.31 to 2.31.62 Updates `software.amazon.awssdk:lambda` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:kinesis` from 2.30.31 to 2.31.62 Updates `software.amazon.awssdk:cloudwatch` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:xray` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:sqs` from 2.30.31 to 2.31.62 Updates `software.amazon.awssdk:cloudformation` from 2.31.40 to 2.31.62 Updates `software.amazon.awssdk:sts` from 2.31.40 to 2.31.62 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.31.62 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.31.62 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.31.62 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ea31c3caa..ae96e2f8e 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.2.3</lambda.core.version> <lambda.events.version>3.15.0</lambda.events.version> - <aws.sdk.version>2.31.40</aws.sdk.version> + <aws.sdk.version>2.31.62</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 4d4c15db2..b641f3216 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.24.3</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.31.40</aws.sdk.version> + <aws.sdk.version>2.31.62</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From d8443dc003e8d3c666433db9ad31ac76964fca3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:07:36 +0200 Subject: [PATCH 0693/1008] build(deps): bump commons-io:commons-io from 2.16.1 to 2.19.0 (#1852) Bumps commons-io:commons-io from 2.16.1 to 2.19.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 779476eae..708a57d4e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -103,7 +103,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.16.1</version> + <version>2.19.0</version> </dependency> <dependency> From c67848d6f0293392762232f670c688f3c635677f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:08:10 +0200 Subject: [PATCH 0694/1008] build(deps): bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions (#1853) Bumps org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions from 0.1.0 to 0.2.0. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions dependency-version: 0.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index f1ae54c46..50042c8ea 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -139,7 +139,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ae96e2f8e..84a8bbe1a 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -136,7 +136,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index e9f2318a9..69ab7c86c 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -124,7 +124,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index db4fbc2b3..87c7d51f4 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -109,7 +109,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index b9b9304b3..312074f5d 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -110,7 +110,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 82be84f06..eed3bacc4 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -110,7 +110,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index b5864de27..430087957 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -169,7 +169,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> From 8b24f270bfc0d00593e93331ece61d3ac7d14445 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:08:38 +0200 Subject: [PATCH 0695/1008] build(deps): bump org.junit.jupiter:junit-jupiter-engine (#1865) Bumps [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.9.3 to 5.13.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.3...r5.13.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 5.13.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 06dba9c56..ed4e1b7b6 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -88,7 +88,7 @@ <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> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 6c152479e..30ff856ed 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -62,7 +62,7 @@ <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> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 9beb3d014..b1819ab21 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -65,7 +65,7 @@ <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> From c72e4c245faeab6f3fb87368d60192230af30e49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:19:39 +0200 Subject: [PATCH 0696/1008] build(deps): bump org.junit-pioneer:junit-pioneer from 1.9.1 to 2.0.0 (#1884) Dependabot couldn't find the original pull request head commit, 0d85e87e3a1668c4007abb86a845439f6d3691b5. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b641f3216..12d779423 100644 --- a/pom.xml +++ b/pom.xml @@ -311,7 +311,7 @@ <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> - <version>1.9.1</version> + <version>2.0.0</version> <scope>test</scope> </dependency> <dependency> From 6bc057a3efd900e72bccedf690113a70b9a791d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:22:30 +0200 Subject: [PATCH 0697/1008] build(deps): bump com.amazonaws:aws-lambda-java-core from 1.2.3 to 1.3.0 (#1892) Bumps [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs) from 1.2.3 to 1.3.0. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-core dependency-version: 1.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 50042c8ea..53db49e17 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 84a8bbe1a..62c60b00b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -12,7 +12,7 @@ <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <lambda.core.version>1.2.3</lambda.core.version> + <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.15.0</lambda.events.version> <aws.sdk.version>2.31.62</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 69ab7c86c..9e38e64e1 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 25517be25..99aeefee8 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -34,7 +34,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 87c7d51f4..62bff0cd4 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 312074f5d..a042e098e 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index eed3bacc4..418dc20af 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 430087957..932da03b4 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -47,7 +47,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index ed4e1b7b6..f0742e86e 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -34,7 +34,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 30ff856ed..a4f8d975b 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 35d0f76d1..bd249bb25 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -26,7 +26,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index b1819ab21..272ff663f 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.2.3</version> + <version>1.3.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/pom.xml b/pom.xml index 12d779423..dac6214c4 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ <aws.xray.recorder.version>2.18.2</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.3</lambda.core.version> + <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.0</lambda.events.version> <lambda.serial.version>1.1.5</lambda.serial.version> <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> From 484cf77d3e77d36fa9fc3e3064ecb3a726941053 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:24:37 +0200 Subject: [PATCH 0698/1008] build(deps): bump log4j.version from 2.24.0 to 2.24.3 (#1893) Bumps `log4j.version` from 2.24.0 to 2.24.3. Updates `org.apache.logging.log4j:log4j-core` from 2.24.0 to 2.24.3 Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.24.3 to 2.24.3 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.24.0 to 2.24.3 Updates `org.apache.logging.log4j:log4j-api` from 2.24.0 to 2.24.3 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.24.0 to 2.24.3 Updates `org.apache.logging.log4j:log4j-jcl` from 2.24.3 to 2.24.3 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.24.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-version: 2.24.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-version: 2.24.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.24.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-version: 2.24.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-version: 2.24.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 99aeefee8..aef211af6 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -9,7 +9,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.24.0</log4j.version> + <log4j.version>2.24.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> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index f0742e86e..9ce76725b 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -8,7 +8,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> <properties> - <log4j.version>2.24.0</log4j.version> + <log4j.version>2.24.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> From 4e6aa451cb32995d502690cf1ed362db51794afe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:25:23 +0200 Subject: [PATCH 0699/1008] build(deps): bump com.amazonaws:aws-lambda-java-events (#1913) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-events dependency-version: 3.16.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 62c60b00b..f7f48e309 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -13,7 +13,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> - <lambda.events.version>3.15.0</lambda.events.version> + <lambda.events.version>3.16.0</lambda.events.version> <aws.sdk.version>2.31.62</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> From d9ee495a7221c2ea0c1d10c1d74a6b87d84b2454 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 10 Jul 2025 11:21:47 +0200 Subject: [PATCH 0700/1008] build(deps): Move wiremock to new version with patched CVEs. (#1923) --- pom.xml | 6 +++--- powertools-cloudformation/pom.xml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index dac6214c4..b450f5b8e 100644 --- a/pom.xml +++ b/pom.xml @@ -374,9 +374,9 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.github.tomakehurst</groupId> - <artifactId>wiremock-jre8</artifactId> - <version>2.35.2</version> + <groupId>org.wiremock</groupId> + <artifactId>wiremock</artifactId> + <version>3.13.1</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 1f3329325..c30b47884 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> @@ -91,8 +91,8 @@ <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> </dependencies> From 3f23077c76aab267a652a023cb108d74ae559eba Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 10 Jul 2025 13:18:10 +0200 Subject: [PATCH 0701/1008] chore(ci): remove v2 dependabot configuration. Restore OSSF scorecard workflow. (#1924) --- .github/dependabot.yml | 13 ------ .github/workflows/security-scorecard.yml | 57 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/security-scorecard.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 28adf2bc1..73f454162 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,16 +7,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"] - - - package-ecosystem: "maven" - directory: "/" - target-branch: "v2" - schedule: - interval: "weekly" - labels: - - "maven" - - "dependencies" diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml new file mode 100644 index 000000000..28c1ab261 --- /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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Run Analysis + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + repo_token: ${{ secrets.SCORECARD_TOKEN }} + - name: Upload Results + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + - name: Upload to Code-Scanning + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 + with: + sarif_file: results.sarif From 0bd73b605e017d437e33d1100a13f22b6c78d99a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:14:05 +0200 Subject: [PATCH 0702/1008] build(deps): bump aws.sdk.version from 2.31.62 to 2.31.78 (#1925) Bumps `aws.sdk.version` from 2.31.62 to 2.31.78. Updates `software.amazon.awssdk:bom` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:http-client-spi` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:url-connection-client` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:dynamodb` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:s3` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:lambda` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:kinesis` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:cloudwatch` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:xray` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:sqs` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:cloudformation` from 2.31.62 to 2.31.78 Updates `software.amazon.awssdk:sts` from 2.31.62 to 2.31.78 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.31.78 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.31.78 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index f7f48e309..7b0c9a21f 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.0</lambda.events.version> - <aws.sdk.version>2.31.62</aws.sdk.version> + <aws.sdk.version>2.31.78</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index b450f5b8e..2bed1815a 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.24.3</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.17.2</jackson.version> - <aws.sdk.version>2.31.62</aws.sdk.version> + <aws.sdk.version>2.31.78</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 3afe39e0168d7ea2922e4347808483fef1ad58fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:14:24 +0200 Subject: [PATCH 0703/1008] build(deps): bump org.assertj:assertj-core from 3.26.3 to 3.27.3 (#1926) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.26.3 to 3.27.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.26.3...assertj-build-3.27.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2bed1815a..5f862eb2c 100644 --- a/pom.xml +++ b/pom.xml @@ -323,7 +323,7 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.26.3</version> + <version>3.27.3</version> <scope>test</scope> <exclusions> <exclusion> From a666d94c2486929bb47adcb6a91008fbe50011b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:15:05 +0200 Subject: [PATCH 0704/1008] build(deps): bump com.amazonaws:aws-lambda-java-runtime-interface-client (#1927) Bumps [com.amazonaws:aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs) from 2.1.1 to 2.8.2. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-runtime-interface-client dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index aef211af6..f5539567b 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.1.1</version> + <version>2.8.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 9ce76725b..c7aecd7e3 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.1.1</version> + <version>2.8.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> From 49c8e53bdb0f9fa822b3a734bdcd22d05838dba1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:15:51 +0200 Subject: [PATCH 0705/1008] build(deps-dev): bump com.amazonaws:amazon-sqs-java-extended-client-lib (#1929) Bumps [com.amazonaws:amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) from 2.1.0 to 2.1.2. - [Release notes](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases) - [Commits](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/compare/2.1.0...2.1.2) --- updated-dependencies: - dependency-name: com.amazonaws:amazon-sqs-java-extended-client-lib dependency-version: 2.1.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 708a57d4e..dbeb8b1ca 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -84,7 +84,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - <version>2.1.0</version> + <version>2.1.2</version> <scope>test</scope> </dependency> From b7f70e6443aa86ddaf4e240b430d7f11d64be538 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:44:23 +0200 Subject: [PATCH 0706/1008] build(deps): bump jackson.version from 2.17.2 to 2.19.1 (#1928) Bumps `jackson.version` from 2.17.2 to 2.19.1. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.17.2 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.17.2 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.17.2...jackson-core-2.19.1) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.17.2 to 2.19.1 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f862eb2c..e86602f2f 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.24.3</log4j.version> <slf4j.version>2.0.7</slf4j.version> - <jackson.version>2.17.2</jackson.version> + <jackson.version>2.19.1</jackson.version> <aws.sdk.version>2.31.78</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> From 0d478de8a3da89e1b8da31136d997b466daef8f7 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 11 Jul 2025 11:28:26 +0200 Subject: [PATCH 0707/1008] build(deps): Upgrade DynamoDBLocal to 2.6.0 (#1931) * Upgrade DynamoDBLocal to 2.6.0 * Downgrade DynamoDBLocal to 2.2.0 to keep Java11 support --- docs/utilities/idempotency.md | 4 +++- .../powertools-idempotency-dynamodb/pom.xml | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 3953949d1..83f256e6b 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -983,7 +983,9 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ <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 --> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 4ae37f00c..4dd59a476 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -14,8 +14,8 @@ --> <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"> + 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> @@ -62,7 +62,9 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>DynamoDBLocal</artifactId> - <version>[1.12,2.0)</version> + <!-- >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> <!--Needed when building locally on M1 Mac--> @@ -85,7 +87,7 @@ <manifestEntries> <Automatic-Module-Name>software.amazon.awssdk.enhanced.dynamodb</Automatic-Module-Name> </manifestEntries> - </archive> + </archive> </configuration> </plugin> <plugin> @@ -123,4 +125,4 @@ </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> From 22b8e6cf9c56d44665d991adb591b510432d023a Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 11 Jul 2025 11:53:08 +0200 Subject: [PATCH 0708/1008] build(deps): Upgrade logback. (#1933) --- powertools-logging/powertools-logging-logback/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index cb2054b87..d729dba23 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -28,11 +28,11 @@ <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> - <version>1.5.7</version> + <version>1.5.18</version> <exclusions> <exclusion> <groupId>com.sun.mail</groupId> - <artifactId>javax.mail</artifactId> + <artifactId>javax.mail</artifactId> </exclusion> </exclusions> </dependency> From 0f3615079699e2bff01cae9098858d6e2ae1ad27 Mon Sep 17 00:00:00 2001 From: Jeroen Reijn <j.reijn@gmail.com> Date: Fri, 11 Jul 2025 13:07:34 +0200 Subject: [PATCH 0709/1008] feat(serialization): Add GraalVM metadata configuration (#1905) * feat(serialization): Add graalvm metadata. Move metadata for tests to test package. * feat(serialization): Split SAM and SAM native example * feat(serialization): Update documentation and makefile * feat(serialization): Bump version * Update examples/powertools-examples-serialization/sam-graalvm/README.md Fix documentation url Co-authored-by: Philipp Page <philipp.page@yahoo.de> * Update examples/powertools-examples-serialization/sam-graalvm/README.md Made URL for curl command generic instead of containing specific ids or regions Co-authored-by: Philipp Page <philipp.page@yahoo.de> * feat(serialization): Bump lambda-java-core and runtime-interface to match other dependencies * feat(serialization): Update metadata to reflect new lambda-java-runtime-client changes --------- Co-authored-by: Philipp Page <philipp.page@yahoo.de> --- examples/pom.xml | 3 +- .../sam-graalvm/Dockerfile | 14 +++ .../sam-graalvm/Makefile | 13 ++ .../sam-graalvm/README.md | 111 ++++++++++++++++++ .../events/APIGatewayEvent.json | 0 .../{ => sam-graalvm}/events/SQSEvent.json | 0 .../sam-graalvm/pom.xml | 89 ++++++++++++++ .../sam-graalvm/src/main/config/bootstrap | 4 + ...GatewayRequestDeserializationFunction.java | 0 .../java/org/demo/serialization/Product.java | 0 .../SQSEventDeserializationFunction.java | 0 .../aws-lambda-java-core/reflect-config.json | 13 ++ .../reflect-config.json | 35 ++++++ .../jni-config.json | 11 ++ .../native-image.properties | 1 + .../reflect-config.json | 38 ++++++ .../resource-config.json | 19 +++ .../reflect-config.json | 25 ++++ .../helloworld/native-image.properties | 3 + .../helloworld/reflect-config.json | 29 +++++ .../helloworld/resource-config.json | 7 ++ .../src/main/resources/log4j2.xml | 0 .../sam-graalvm/template.yaml | 64 ++++++++++ .../{ => sam}/README.md | 2 +- .../sam/events/APIGatewayEvent.json | 63 ++++++++++ .../sam/events/SQSEvent.json | 39 ++++++ .../{ => sam}/pom.xml | 2 +- ...GatewayRequestDeserializationFunction.java | 53 +++++++++ .../java/org/demo/serialization/Product.java | 63 ++++++++++ .../SQSEventDeserializationFunction.java | 40 +++++++ .../sam/src/main/resources/log4j2.xml | 16 +++ .../{ => sam}/template.yaml | 0 powertools-serialization/pom.xml | 38 +++--- .../powertools-serialization/jni-config.json | 18 --- .../reflect-config.json | 45 +------ .../resource-config.json | 32 +---- .../reflect-config.json | 45 +++++++ .../resource-config.json | 51 ++++++++ 38 files changed, 873 insertions(+), 113 deletions(-) create mode 100644 examples/powertools-examples-serialization/sam-graalvm/Dockerfile create mode 100644 examples/powertools-examples-serialization/sam-graalvm/Makefile create mode 100644 examples/powertools-examples-serialization/sam-graalvm/README.md rename examples/powertools-examples-serialization/{ => sam-graalvm}/events/APIGatewayEvent.json (100%) rename examples/powertools-examples-serialization/{ => sam-graalvm}/events/SQSEvent.json (100%) create mode 100644 examples/powertools-examples-serialization/sam-graalvm/pom.xml create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/config/bootstrap rename examples/powertools-examples-serialization/{ => sam-graalvm}/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java (100%) rename examples/powertools-examples-serialization/{ => sam-graalvm}/src/main/java/org/demo/serialization/Product.java (100%) rename examples/powertools-examples-serialization/{ => sam-graalvm}/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java (100%) create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/native-image.properties create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json create mode 100644 examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json rename examples/powertools-examples-serialization/{ => sam-graalvm}/src/main/resources/log4j2.xml (100%) create mode 100644 examples/powertools-examples-serialization/sam-graalvm/template.yaml rename examples/powertools-examples-serialization/{ => sam}/README.md (97%) create mode 100644 examples/powertools-examples-serialization/sam/events/APIGatewayEvent.json create mode 100644 examples/powertools-examples-serialization/sam/events/SQSEvent.json rename examples/powertools-examples-serialization/{ => sam}/pom.xml (96%) create mode 100644 examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java create mode 100644 examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/Product.java create mode 100644 examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java create mode 100644 examples/powertools-examples-serialization/sam/src/main/resources/log4j2.xml rename examples/powertools-examples-serialization/{ => sam}/template.yaml (100%) delete mode 100644 powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json create mode 100644 powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/reflect-config.json create mode 100644 powertools-serialization/src/test/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/resource-config.json diff --git a/examples/pom.xml b/examples/pom.xml index cd076d099..2487a4d14 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -38,7 +38,8 @@ <module>powertools-examples-idempotency</module> <module>powertools-examples-parameters/sam</module> <module>powertools-examples-parameters/sam-graalvm</module> - <module>powertools-examples-serialization</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> diff --git a/examples/powertools-examples-serialization/sam-graalvm/Dockerfile b/examples/powertools-examples-serialization/sam-graalvm/Dockerfile new file mode 100644 index 000000000..a690606ad --- /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:latest + +#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..370a3ec60 --- /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.1.1</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.3.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-events</artifactId> + <version>3.16.0</version> + </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.2</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.2</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.10.1</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 100% 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 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 100% 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 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..962962055 --- /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,38 @@ +[ + { + "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 + }, + { + "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", + "fields":[{"name":"IS_COLD_START"},{"name":"SERVICE_NAME"}] + } +] \ 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/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/src/main/resources/log4j2.xml b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-serialization/src/main/resources/log4j2.xml rename to examples/powertools-examples-serialization/sam-graalvm/src/main/resources/log4j2.xml 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/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml similarity index 96% rename from examples/powertools-examples-serialization/pom.xml rename to examples/powertools-examples-serialization/sam/pom.xml index bd249bb25..8c17b2a86 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <version>2.1.1</version> - <artifactId>powertools-examples-serialization</artifactId> + <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> 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/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java new file mode 100644 index 000000000..79097e19c --- /dev/null +++ b/examples/powertools-examples-serialization/sam/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.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 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 java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class SQSEventDeserializationFunction implements RequestHandler<SQSEvent, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(SQSEventDeserializationFunction.class); + + 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(); + } +} + 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/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 891a1582a..bb2f0fb34 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -70,6 +70,26 @@ <scope>test</scope> </dependency> </dependencies> + + <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>${aspectj-maven-plugin.version}</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + <profiles> <profile> <id>generate-graalvm-files</id> @@ -159,23 +179,5 @@ </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>${aspectj-maven-plugin.version}</version> - <configuration> - <skip>true</skip> - </configuration> - </plugin> - </plugins> - </build> </project> diff --git a/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json b/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json deleted file mode 100644 index 079c02a4d..000000000 --- a/powertools-serialization/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization/jni-config.json +++ /dev/null @@ -1,18 +0,0 @@ -[ -{ - "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.management.VMManagementImpl", - "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] -} -] 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 index 1a4f89735..e20bd748f 100644 --- 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 @@ -309,7 +309,7 @@ "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":"setMessageAttributes","parameterTypes":["java.util.Map"] }, {"name":"setMessageId","parameterTypes":["java.lang.String"] }, {"name":"setReceiptHandle","parameterTypes":["java.lang.String"] }] + "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", @@ -423,48 +423,5 @@ { "name":"org.joda.time.DateTime", "methods":[{"name":"parse","parameterTypes":["java.lang.String"] }] -}, -{ - "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/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 index d5bd7e14c..58b01cd07 100644 --- 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 @@ -6,36 +6,10 @@ "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":"\\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":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/Europe/Berlin\\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" }, { @@ -75,11 +49,9 @@ }, { "pattern":"\\Qcom/amazonaws/services/lambda/runtime/serialization/factories/sqs_event_no_body.json\\E" }, { - "pattern":"\\Qorg/joda/time/tz/data/Europe/Berlin\\E" + "pattern":"\\Qorg/joda/time/tz/data/Europe/Amsterdam\\E" }, { "pattern":"\\Qorg/joda/time/tz/data/ZoneInfoMap\\E" - }, { - "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }]}, "bundles":[] } 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":[] +} From f8a9ede9ef41dac2f5b8fb6f690bf9ad41e50366 Mon Sep 17 00:00:00 2001 From: Jeroen Reijn <j.reijn@gmail.com> Date: Fri, 11 Jul 2025 14:02:21 +0200 Subject: [PATCH 0710/1008] fix(examples): Fix GraalVM metadata after common runtime client changes (#1935) * fix(parameters): Update metadata to reflect new lambda-java-runtime-client changes * fix(core-utilties): Update metadata to reflect new lambda-java-runtime-client changes --- .../jni-config.json | 4 ++-- .../reflect-config.json | 4 ++-- .../resource-config.json | 8 ++++---- .../jni-config.json | 4 ++-- .../reflect-config.json | 4 ++-- .../resource-config.json | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) 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 index d30696750..91be72f7a 100644 --- 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 @@ -4,8 +4,8 @@ "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name":"content"}], + "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/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 index 106edef38..10152cc64 100644 --- 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 @@ -27,8 +27,8 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name":"content"}], + "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/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 index 7cc78a494..1062b4249 100644 --- 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 @@ -2,16 +2,16 @@ "resources": { "includes": [ { - "pattern": "\\Qaarch64/aws-lambda-runtime-interface-client.glibc.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" }, { - "pattern": "\\Qaarch64/aws-lambda-runtime-interface-client.musl.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" }, { - "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.glibc.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" }, { - "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.musl.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" } ] }, 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 index d30696750..91be72f7a 100644 --- 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 @@ -4,8 +4,8 @@ "methods":[{"name":"<init>","parameterTypes":["java.lang.String","int"] }] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name":"content"}], + "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/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 index 106edef38..10152cc64 100644 --- 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 @@ -27,8 +27,8 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name":"content"}], + "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/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 index 7cc78a494..1062b4249 100644 --- 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 @@ -2,16 +2,16 @@ "resources": { "includes": [ { - "pattern": "\\Qaarch64/aws-lambda-runtime-interface-client.glibc.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" }, { - "pattern": "\\Qaarch64/aws-lambda-runtime-interface-client.musl.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" }, { - "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.glibc.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" }, { - "pattern": "\\Qx86_64/aws-lambda-runtime-interface-client.musl.so\\E" + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" } ] }, From 3a7b67bb21e875135a2d52b12dcb5fab2d670888 Mon Sep 17 00:00:00 2001 From: Vishesh Ruparelia <visheshruparelia18@gmail.com> Date: Fri, 11 Jul 2025 20:22:20 +0530 Subject: [PATCH 0711/1008] feat(batch): add support for batch execution in parallel with custom Executor (#1900) * feat: add support for batch execution in parallel with custom Executor * pmd_analyse fix * add docs * Update docs/utilities/batch.md Co-authored-by: Philipp Page <philipp.page@yahoo.de> --------- Co-authored-by: Philipp Page <github@philipp.page> Co-authored-by: Philipp Page <philipp.page@yahoo.de> --- docs/utilities/batch.md | 25 +++++++ .../dynamo/DynamoDBStreamBatchHandler.java | 2 +- .../DynamoDBStreamBatchHandlerParallel.java | 37 ++++++++++ .../batch/kinesis/KinesisBatchHandler.java | 2 +- .../kinesis/KinesisBatchHandlerParallel.java | 39 +++++++++++ .../batch/sqs/AbstractSqsBatchHandler.java | 3 +- .../batch/sqs/SqsBatchHandlerParallel.java | 37 ++++++++++ .../batch/handler/BatchMessageHandler.java | 13 ++++ .../handler/DynamoDbBatchMessageHandler.java | 37 ++++++++-- .../KinesisStreamsBatchMessageHandler.java | 37 ++++++++-- .../batch/handler/SqsBatchMessageHandler.java | 48 ++++++++++--- .../batch/internal/MultiThreadMDC.java | 7 ++ .../batch/DdbBatchProcessorTest.java | 70 +++++++++++++++---- .../batch/KinesisBatchProcessorTest.java | 69 ++++++++++++++---- .../batch/SQSBatchProcessorTest.java | 68 ++++++++++++++---- 15 files changed, 425 insertions(+), 69 deletions(-) create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/dynamo/DynamoDBStreamBatchHandlerParallel.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandlerParallel.java create mode 100644 examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/SqsBatchHandlerParallel.java diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 7693ac98f..3f9b6e53d 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -510,6 +510,31 @@ used with SQS FIFO. In that case, an `UnsupportedOperationException` is thrown. } } ``` +=== "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 + } + } + ``` ## Handling Messages 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 e3c27c093..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 @@ -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/kinesis/KinesisBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java index f5d7102b5..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 @@ -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/sqs/AbstractSqsBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/sqs/AbstractSqsBatchHandler.java index 25dba47bb..ee33b50fd 100644 --- 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 @@ -42,11 +42,10 @@ public class AbstractSqsBatchHandler { /** * Simulate some processing (I/O + S3 put request) * @param p deserialized product - * @param context Lambda context */ @Logging @Tracing - protected void processMessage(Product p, Context context) { + protected void processMessage(Product p) { TracingUtils.putAnnotation("productId", p.getId()); TracingUtils.putAnnotation("Thread", Thread.currentThread().getName()); MDC.put("product", String.valueOf(p.getId())); 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/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 18d74bb25..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. * @@ -50,4 +53,14 @@ public interface BatchMessageHandler<E, R> { * @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 4b03d0947..df7179a88 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 @@ -17,8 +17,12 @@ 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.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -66,7 +70,9 @@ public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context .parallelStream() // Parallel processing .map(eventRecord -> { multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - return processBatchItem(eventRecord, context); + Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); + multiThreadMDC.removeThread(Thread.currentThread().getName()); + return failureOpt; }) .filter(Optional::isPresent) .map(Optional::get) @@ -75,6 +81,23 @@ public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); } + @Override + public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context context, Executor executor) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new ArrayList<>(); + List<CompletableFuture<Void>> futures = event.getRecords().stream() + .map(eventRecord -> CompletableFuture.runAsync(() -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); + failureOpt.ifPresent(batchItemFailures::add); + 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()); @@ -86,19 +109,19 @@ private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem(Dynamod this.successHandler.accept(streamRecord); } return Optional.empty(); - } catch (Throwable t) { + } catch (Exception e) { String sequenceNumber = streamRecord.getDynamodb().getSequenceNumber(); LOGGER.error("Error while processing record with id {}: {}, adding it to batch item failures", - sequenceNumber, t.getMessage()); - LOGGER.error("Error was", t); + 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, t); - } catch (Throwable t2) { - LOGGER.warn("failureHandler threw handling failure", t2); + this.failureHandler.accept(streamRecord, e); + } catch (Exception e2) { + LOGGER.warn("failureHandler threw handling failure", e2); } } return Optional.of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(sequenceNumber).build()); 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 7b4179de7..233830462 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 @@ -18,8 +18,12 @@ 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.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -77,7 +81,9 @@ public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context c .parallelStream() // Parallel processing .map(eventRecord -> { multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - return processBatchItem(eventRecord, context); + Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); + multiThreadMDC.removeThread(Thread.currentThread().getName()); + return failureOpt; }) .filter(Optional::isPresent) .map(Optional::get) @@ -86,6 +92,23 @@ public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context c return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); } + @Override + public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context context, Executor executor) { + MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + + List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new ArrayList<>(); + List<CompletableFuture<Void>> futures = event.getRecords().stream() + .map(eventRecord -> CompletableFuture.runAsync(() -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); + failureOpt.ifPresent(batchItemFailures::add); + 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()); @@ -102,19 +125,19 @@ private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem(Kinesis this.successHandler.accept(eventRecord); } return Optional.empty(); - } catch (Throwable t) { + } catch (Exception e) { String sequenceNumber = eventRecord.getEventID(); LOGGER.error("Error while processing record with eventID {}: {}, adding it to batch item failures", - sequenceNumber, t.getMessage()); - LOGGER.error("Error was", t); + 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, t); - } catch (Throwable t2) { - LOGGER.warn("failureHandler threw handling failure", t2); + this.failureHandler.accept(eventRecord, e); + } catch (Exception e2) { + LOGGER.warn("failureHandler threw handling failure", e2); } } 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 2dfb0a28e..ccb6a6dd7 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 @@ -15,15 +15,20 @@ 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.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.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; + +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; @@ -99,7 +104,7 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { @Override public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) { - if (!event.getRecords().isEmpty() && event.getRecords().get(0).getAttributes().get(MESSAGE_GROUP_ID_KEY) != null) { + if (isFIFOEnabled(event)) { throw new UnsupportedOperationException("FIFO queues are not supported in parallel mode, use the processBatch method instead"); } @@ -109,7 +114,9 @@ public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) .map(sqsMessage -> { multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - return processBatchItem(sqsMessage, context); + Optional<SQSBatchResponse.BatchItemFailure> failureOpt = processBatchItem(sqsMessage, context); + multiThreadMDC.removeThread(Thread.currentThread().getName()); + return failureOpt; }) .filter(Optional::isPresent) .map(Optional::get) @@ -118,6 +125,27 @@ public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) 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(); + List<SQSBatchResponse.BatchItemFailure> batchItemFailures = new ArrayList<>(); + List<CompletableFuture<Void>> futures = event.getRecords().stream() + .map(eventRecord -> CompletableFuture.runAsync(() -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + Optional<SQSBatchResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); + failureOpt.ifPresent(batchItemFailures::add); + 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()); @@ -134,22 +162,26 @@ private Optional<SQSBatchResponse.BatchItemFailure> processBatchItem(SQSEvent.SQ this.successHandler.accept(message); } return Optional.empty(); - } catch (Throwable t) { + } catch (Exception e) { LOGGER.error("Error while processing message with messageId {}: {}, adding it to batch item failures", - message.getMessageId(), t.getMessage()); - LOGGER.error("Error was", t); + 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, t); - } catch (Throwable t2) { - LOGGER.warn("failureHandler threw handling failure", t2); + 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/MultiThreadMDC.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/MultiThreadMDC.java index df1c2e7a0..b2b85044b 100644 --- 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 @@ -44,4 +44,11 @@ public void copyMDCToThread(String thread) { 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/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java b/powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/DdbBatchProcessorTest.java index 6bb247323..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 @@ -23,7 +23,12 @@ 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; @@ -43,6 +48,8 @@ public void 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) { @@ -78,6 +85,25 @@ private void processMessageInParallelFailsForFixedMessage(DynamodbEvent.Dynamodb } } + 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) void batchProcessingSucceedsAndReturns(DynamodbEvent event) { @@ -96,13 +122,7 @@ void batchProcessingSucceedsAndReturns(DynamodbEvent event) { @ParameterizedTest @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) void parallelBatchProcessingSucceedsAndReturns(DynamodbEvent event) { - // Arrange - BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() - .withDynamoDbBatchHandler() - .buildWithRawMessageHandler(this::processMessageInParallelSucceeds); - - // Act - StreamsEventResponse dynamodbBatchResponse = handler.processBatchInParallel(event, context); + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, null); // Assert assertThat(dynamodbBatchResponse.getBatchItemFailures()).isEmpty(); @@ -129,13 +149,7 @@ void shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent even @ParameterizedTest @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) void parallelBatchProcessing_shouldAddMessageToBatchFailure_whenException_withMessage(DynamodbEvent event) { - // Arrange - BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() - .withDynamoDbBatchHandler() - .buildWithRawMessageHandler(this::processMessageInParallelFailsForFixedMessage); - - // Act - StreamsEventResponse dynamodbBatchResponse = handler.processBatchInParallel(event, context); + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, null); // Assert assertThat(dynamodbBatchResponse.getBatchItemFailures()).hasSize(1); @@ -196,4 +210,32 @@ void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(DynamodbEvent ev 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 059a4d2d0..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 @@ -23,7 +23,12 @@ 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; @@ -81,6 +86,25 @@ private void processMessageInParallelFailsForFixedMessage(KinesisEvent.KinesisEv } } + 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) { @@ -106,13 +130,7 @@ void batchProcessingSucceedsAndReturns(KinesisEvent event) { @ParameterizedTest @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) void batchProcessingInParallelSucceedsAndReturns(KinesisEvent event) { - // Arrange - BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() - .withKinesisBatchHandler() - .buildWithRawMessageHandler(this::processMessageInParallelSucceeds); - - // Act - StreamsEventResponse kinesisBatchResponse = handler.processBatchInParallel(event, context); + StreamsEventResponse kinesisBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, null); // Assert assertThat(kinesisBatchResponse.getBatchItemFailures()).isEmpty(); @@ -140,13 +158,7 @@ void shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event @ParameterizedTest @Event(value = "kinesis_event_big.json", type = KinesisEvent.class) void batchProcessingInParallel_shouldAddMessageToBatchFailure_whenException_withMessage(KinesisEvent event) { - // Arrange - BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() - .withKinesisBatchHandler() - .buildWithRawMessageHandler(this::processMessageInParallelFailsForFixedMessage); - - // Act - StreamsEventResponse kinesisBatchResponse = handler.processBatchInParallel(event, context); + StreamsEventResponse kinesisBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, null); // Assert assertThat(kinesisBatchResponse.getBatchItemFailures()).hasSize(1); @@ -227,4 +239,33 @@ void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(KinesisEvent eve "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 7dd51374e..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 @@ -23,7 +23,12 @@ 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; @@ -45,7 +50,7 @@ public void clear() { private void processMessageSucceeds(SQSEvent.SQSMessage sqsMessage) { } - private void processMessageInParallelSucceeds(SQSEvent.SQSMessage sqsMessage) { + private void processMessageInParallelSucceeds(SQSEvent.SQSMessage sqsMessage, Context context) { String thread = Thread.currentThread().getName(); if (!threadList.contains(thread)) { threadList.add(thread); @@ -86,6 +91,25 @@ private void processMessageFailsForFixedProduct(Product product, Context context } } + 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) void batchProcessingSucceedsAndReturns(SQSEvent event) { @@ -104,13 +128,7 @@ void batchProcessingSucceedsAndReturns(SQSEvent event) { @ParameterizedTest @Event(value = "sqs_event_big.json", type = SQSEvent.class) void parallelBatchProcessingSucceedsAndReturns(SQSEvent event) { - // Arrange - BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() - .withSqsBatchHandler() - .buildWithRawMessageHandler(this::processMessageInParallelSucceeds); - - // Act - SQSBatchResponse sqsBatchResponse = handler.processBatchInParallel(event, context); + SQSBatchResponse sqsBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelSucceeds, null); // Assert assertThat(sqsBatchResponse.getBatchItemFailures()).isEmpty(); @@ -137,13 +155,7 @@ void shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { @ParameterizedTest @Event(value = "sqs_event_big.json", type = SQSEvent.class) void parallelBatchProcessing_shouldAddMessageToBatchFailure_whenException_withMessage(SQSEvent event) { - // Arrange - BatchMessageHandler<SQSEvent, SQSBatchResponse> handler = new BatchMessageHandlerBuilder() - .withSqsBatchHandler() - .buildWithRawMessageHandler(this::processMessageInParallelFailsForFixedMessage); - - // Act - SQSBatchResponse sqsBatchResponse = handler.processBatchInParallel(event, context); + SQSBatchResponse sqsBatchResponse = testParallelBatchExecution(event, this::processMessageInParallelFailsForFixedMessage, null); // Assert assertThat(sqsBatchResponse.getBatchItemFailures()).hasSize(1); @@ -238,5 +250,31 @@ void failingSuccessHandlerShouldntFailBatchButShouldFailMessage(SQSEvent event) 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); + } + } From 9dcb9b64eaa15157b5356b48b39e632421a92736 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:44:11 +0200 Subject: [PATCH 0712/1008] build(deps): bump org.apache.commons:commons-lang3 from 3.15.0 to 3.18.0 (#1936) Bumps org.apache.commons:commons-lang3 from 3.15.0 to 3.18.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.18.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e86602f2f..108354d82 100644 --- a/pom.xml +++ b/pom.xml @@ -297,7 +297,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.15.0</version> + <version>3.18.0</version> </dependency> <!-- Test dependencies --> From 765f0ac698d1173582d1c63d8727841e19af9711 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:45:42 +0200 Subject: [PATCH 0713/1008] build(deps-dev): bump org.apache.commons:commons-lang3 (#1937) Bumps org.apache.commons:commons-lang3 from 3.15.0 to 3.18.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.18.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 57c8cb207ae2f1fd237453cac278882efcd8eb4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:46:43 +0200 Subject: [PATCH 0714/1008] build(deps): bump aws.sdk.version from 2.30.31 to 2.31.78 (#1938) Bumps `aws.sdk.version` from 2.30.31 to 2.31.78. Updates `software.amazon.awssdk:url-connection-client` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:sdk-core` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:s3` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:kinesis` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:sqs` from 2.30.31 to 2.31.78 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.30.31 to 2.31.78 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.31.78 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.31.78 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.31.78 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 53db49e17..22a8835d3 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.30.31</sdk.version> + <sdk.version>2.31.78</sdk.version> </properties> <dependencies> From b277e65ca8f5993d1e2fa772e2424b88fbe47990 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:48:04 +0200 Subject: [PATCH 0715/1008] build(deps): bump log4j.version from 2.24.3 to 2.25.1 (#1939) Bumps `log4j.version` from 2.24.3 to 2.25.1. Updates `org.apache.logging.log4j:log4j-core` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-api` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-jcl` from 2.24.3 to 2.25.1 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 9e38e64e1..f9f851d8e 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -11,7 +11,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.24.3</log4j.version> + <log4j.version>2.25.1</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index f5539567b..730ac8d21 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -9,7 +9,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.24.3</log4j.version> + <log4j.version>2.25.1</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index c7aecd7e3..df5e39a3e 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -8,7 +8,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> <properties> - <log4j.version>2.24.3</log4j.version> + <log4j.version>2.25.1</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/pom.xml b/pom.xml index 108354d82..057ccf977 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ <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.24.3</log4j.version> + <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.7</slf4j.version> <jackson.version>2.19.1</jackson.version> <aws.sdk.version>2.31.78</aws.sdk.version> From 765ddf27bffb7eb3b15c7e645d6e2358c7aa464b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:48:25 +0200 Subject: [PATCH 0716/1008] build(deps): bump org.sonatype.central:central-publishing-maven-plugin (#1940) Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.7.0 to 0.8.0. - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) --- updated-dependencies: - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 057ccf977..c00e341a6 100644 --- a/pom.xml +++ b/pom.xml @@ -506,7 +506,7 @@ <plugin> <groupId>org.sonatype.central</groupId> <artifactId>central-publishing-maven-plugin</artifactId> - <version>0.7.0</version> + <version>0.8.0</version> <extensions>true</extensions> <configuration> <publishingServerId>central</publishingServerId> From ff87ecced7b067ea6a479229a94ff6236269dbc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:48:46 +0200 Subject: [PATCH 0717/1008] build(deps): bump org.codehaus.mojo:build-helper-maven-plugin (#1941) Bumps [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.6.0...3.6.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-kafka/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 5eb79913b..02a52a2f4 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -203,7 +203,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <id>add-test-source</id> From 08c9db8632cd44453e52f5f5dc04c4ba0b456867 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:29:17 +0200 Subject: [PATCH 0718/1008] chore(ci): bump version to 2.2.0 (#1942) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 43 files changed, 49 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 7e33bf19e..3ea636922 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 2487a4d14..4bea96700 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 22a8835d3..abc4d660b 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 7b0c9a21f..d6c2dbca0 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index f9f851d8e..1f0f9f4ef 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 4af44d146..d6a4dc3cf 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.162.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 04368e29c..4cee5ecfa 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.1.1' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.1.1' - aspect 'software.amazon.lambda:powertools-metrics:2.1.1' + aspect 'software.amazon.lambda:powertools-tracing:2.2.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.2.0' + aspect 'software.amazon.lambda:powertools-metrics:2.2.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 2b3d184d7..e769c3c45 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.1.1") - aspect("software.amazon.lambda:powertools-logging-log4j:2.1.1") - aspect("software.amazon.lambda:powertools-metrics:2.1.1") + aspect("software.amazon.lambda:powertools-tracing:2.2.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.2.0") + aspect("software.amazon.lambda:powertools-metrics:2.2.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 730ac8d21..6fbe62ec2 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 62bff0cd4..ba436c650 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index a042e098e..b62f193aa 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 418dc20af..a09b26ac6 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 932da03b4..a56eae46d 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 6921f2793..4ea02a649 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index df5e39a3e..3336f6bd1 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index a4f8d975b..c729ed83c 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 370a3ec60..2239515f3 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 8c17b2a86..ed70081a2 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 272ff663f..6955fc4ea 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.1.1</version> + <version>2.2.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 0008ef9dd..9b70afcb3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,7 +121,7 @@ extra_javascript: extra: powertools: - version: 2.1.1 + version: 2.2.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index c00e341a6..c7e850e32 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index c5036e74a..09589595f 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index c30b47884..a5888bf5d 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 20060bae7..27e2dbfb4 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index dbeb8b1ca..4cf4afc3d 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 029fa4163..f40bc7ee6 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index c8ea35d4b..c8f1e58d6 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 4dd59a476..701259da3 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 02a52a2f4..d0636baf2 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 1b975739d..6b9b258ca 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index af9f066b3..fe32aa534 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 2cfcd967d..fd081b963 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index d729dba23..6dc6167ab 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 3501c7b0e..18b11f5c3 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index d4da33a8d..2429d374c 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 206d259e1..825e50d90 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 3959ff65b..b1bfaa5df 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 9885fcef1..34031ca42 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index e9f33a24b..faecdf157 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 21ce5639e..55a3b9509 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.1.1</version> + <version>2.2.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index bb2f0fb34..e0867b486 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 5239e7b6c..4d8326d6b 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 3ab6e51fc..b51b531fa 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.1.1</version> + <version>2.2.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 78fab5218a34d36ea2d1d6b5fd517ed556e7ea1a Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 21 Jul 2025 13:07:16 +0200 Subject: [PATCH 0719/1008] chore(ci): Add GraalVM E2E tests and GH workflows (#1945) * Add GraalVM support for E2E tests starting with metrics handler. * Add GraalVM E2E test support for Logging and Parameters. Refactor metrics configuration be in parent pom. * Add e2e-graal step to e2e test workflow. * Disable maven download progress logs to reduce amount of logs in GH runner. * Fix sonarlint issue. * Update osv-scanner to 2.1.0. * PIN docker image version. * Add fail-fast: false option to GH workflow. --- .github/workflows/check-e2e.yml | 36 +++- .github/workflows/security-osv.yml | 2 +- powertools-e2e-tests/handlers/logging/pom.xml | 24 ++- .../aws-lambda-java-core/reflect-config.json | 13 ++ .../reflect-config.json | 35 ++++ .../jni-config.json | 11 ++ .../native-image.properties | 1 + .../reflect-config.json | 61 ++++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 +++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + powertools-e2e-tests/handlers/metrics/pom.xml | 24 ++- .../aws-lambda-java-core/reflect-config.json | 13 ++ .../reflect-config.json | 35 ++++ .../jni-config.json | 11 ++ .../native-image.properties | 1 + .../reflect-config.json | 61 ++++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 +++ .../reflect-config.json | 20 ++ .../handlers/parameters/pom.xml | 24 ++- .../aws-lambda-java-core/reflect-config.json | 13 ++ .../reflect-config.json | 35 ++++ .../jni-config.json | 11 ++ .../native-image.properties | 1 + .../reflect-config.json | 61 ++++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 +++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + powertools-e2e-tests/handlers/pom.xml | 51 +++-- powertools-e2e-tests/pom.xml | 35 ++++ .../amazon/lambda/powertools/LoggingE2ET.java | 25 +-- .../amazon/lambda/powertools/MetricsE2ET.java | 38 ++-- .../lambda/powertools/ParametersE2ET.java | 20 +- .../testutils/DockerConfiguration.java | 180 ++++++++++++++++++ .../powertools/testutils/Infrastructure.java | 52 ++--- .../src/test/resources/docker/Dockerfile | 14 ++ 39 files changed, 1003 insertions(+), 91 deletions(-) create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DockerConfiguration.java create mode 100644 powertools-e2e-tests/src/test/resources/docker/Dockerfile diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index d5c95f156..19500b2e5 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -47,6 +47,7 @@ jobs: id-token: write environment: E2E strategy: + fail-fast: false max-parallel: 3 matrix: java: @@ -70,4 +71,37 @@ jobs: - name: Run e2e test with Maven env: JAVA_VERSION: ${{ matrix.java }} - run: mvn -DskipTests install --file pom.xml && mvn -Pe2e -B verify --file powertools-e2e-tests/pom.xml + 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: 3 + matrix: + java: + - 11 + - 17 + - 21 + + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + 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/security-osv.yml b/.github/workflows/security-osv.yml index 67e2e6e3f..73d6b1835 100644 --- a/.github/workflows/security-osv.yml +++ b/.github/workflows/security-osv.yml @@ -32,4 +32,4 @@ jobs: actions: read contents: read security-events: write - uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@764c91816374ff2d8fc2095dab36eecd42d61638 # v1.9.2 \ No newline at end of file + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b00f71e051ddddc6e46a193c31c8c0bf283bf9e6 # v2.1.0 diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 62f2f7530..f5eacf5c5 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -1,5 +1,5 @@ <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> @@ -30,6 +30,14 @@ <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> @@ -62,4 +70,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/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e69fa735c --- /dev/null +++ b/powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,61 @@ +[ + { + "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 + } +] diff --git a/powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/logging/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/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/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/logging/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/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/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index e543c2cd0..7bdc75591 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -1,5 +1,5 @@ <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> @@ -21,6 +21,14 @@ <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> @@ -57,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/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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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 471e79d8f..46e6dc1e5 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -1,5 +1,5 @@ <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> @@ -25,6 +25,14 @@ <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> @@ -61,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/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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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 988ae3d55..1b3eaf3e0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -1,5 +1,5 @@ <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> @@ -15,13 +15,13 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</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> + <lambda.java.core>1.3.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.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> + <aws.sdk.version>2.32.2</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> @@ -112,6 +112,11 @@ <artifactId>aws-lambda-java-serialization</artifactId> <version>${lambda.java.serialization}</version> </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.2</version> + </dependency> </dependencies> </dependencyManagement> @@ -134,17 +139,18 @@ </goals> <configuration> <transformers> - <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + <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.1.0</version> - </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> + <version>0.1.0</version> + </dependency> </dependencies> </plugin> <plugin> @@ -185,6 +191,29 @@ </dependency> </dependencies> </plugin> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.6</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> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 4cf4afc3d..3bbaca3e2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -234,6 +234,41 @@ </plugins> </build> </profile> + <profile> + <id>e2e-graal</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <version>3.5.3</version> + <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> + </includes> + <excludes> + <exclude>**/TracingE2ET.java</exclude> + </excludes> + <systemPropertyVariables> + <graalvm.enabled>true</graalvm.enabled> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + </build> + </profile> </profiles> </project> 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 f78500c65..ad2c2564f 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,22 +19,25 @@ 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.Timeout; + +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 { +class LoggingE2ET { private static final ObjectMapper objectMapper = new ObjectMapper(); @@ -42,17 +45,17 @@ public class LoggingE2ET { 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(LoggingE2ET.class.getSimpleName()) .tracing(true) .pathToFunction("logging") .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(); @@ -60,14 +63,14 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { + void test_logInfoWithAdditionalKeys() throws JsonProcessingException { // GIVEN String orderId = UUID.randomUUID().toString(); String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId + "\"}}"; 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 2765e0e70..feb9537d5 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 @@ -38,22 +38,22 @@ 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(); @@ -62,14 +62,14 @@ 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 Instant currentTimeTruncatedToMinutes = Instant.now(Clock.systemUTC()).truncatedTo(ChronoUnit.MINUTES); @@ -84,17 +84,17 @@ public void test_recordMetrics() { // THEN MetricsFetcher metricsFetcher = new MetricsFetcher(); List<Double> coldStart = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, - namespace, + NAMESPACE, "ColdStart", Stream.of(new String[][] { { "FunctionName", functionName }, - { "Service", service } }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); + { "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, + 60, NAMESPACE, "orders", Collections.singletonMap("Environment", "test")); assertThat(orderMetrics.get(0)).isEqualTo(2); List<Double> productMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), - invocationResult.getEnd(), 60, namespace, + 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 @@ -102,24 +102,22 @@ public void test_recordMetrics() { assertThat(productMetrics.get(0)).isEqualTo(12); orderMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, - namespace, - "orders", Collections.singletonMap("Service", service)); + 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)); + 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, + 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/testutils/DockerConfiguration.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DockerConfiguration.java new file mode 100644 index 000000000..204d5863a --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DockerConfiguration.java @@ -0,0 +1,180 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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) + .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) + .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 07d816112..c65871a0a 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 @@ -19,13 +19,10 @@ 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.Map; import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,10 +32,8 @@ import software.amazon.awscdk.App; import software.amazon.awscdk.BundlingOptions; -import software.amazon.awscdk.BundlingOutput; import software.amazon.awscdk.CfnOutput; import software.amazon.awscdk.DefaultStackSynthesizer; -import software.amazon.awscdk.DockerVolume; import software.amazon.awscdk.Duration; import software.amazon.awscdk.RemovalPolicy; import software.amazon.awscdk.Stack; @@ -59,6 +54,7 @@ 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; @@ -218,47 +214,31 @@ private Stack createStackWithLambda() { .build()) .build(); - 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/"); - - 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); + 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); 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(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(); @@ -457,17 +437,19 @@ private void synthesize() { private void uploadAssets() { Map<String, Asset> assets = findAssets(); assets.forEach((objectKey, asset) -> { - if (!asset.assetPath.endsWith(".jar")) { + // .zip will be used for GraalVM bundles. + if (!asset.assetPath.endsWith(".jar") && !asset.assetPath.endsWith(".zip")) { + LOG.info("Skipping upload of {}", asset); return; } + 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)); }); 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..a244dd60d --- /dev/null +++ b/powertools-e2e-tests/src/test/resources/docker/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 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 From 84e7ad0dafb2d9b55f6b1b6001845ae0f42e48bd Mon Sep 17 00:00:00 2001 From: Leandro Damascena <lcdama@amazon.pt> Date: Mon, 21 Jul 2025 17:08:40 +0100 Subject: [PATCH 0720/1008] chore(ci): add new dependabot package ecosystems (#1948) * Add new dependabot ecosystems * Addressing Philipp's comments --- .github/dependabot.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 73f454162..c2baff44e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,9 +1,24 @@ version: 2 updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: chore + + - package-ecosystem: docker + directories: + - "/powertools-e2e-tests" + - "/examples" + labels: [ ] + schedule: + interval: daily + - package-ecosystem: "maven" directory: "/" schedule: - interval: "weekly" + interval: "daily" labels: - "maven" - "dependencies" From f25af3c2c782a7117e8a571a0c858e88d873f89b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:50:05 +0200 Subject: [PATCH 0721/1008] chore: bump ossf/scorecard-action from 2.4.0 to 2.4.2 (#1950) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.0 to 2.4.2. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/62b2cac7ed8198b15735ed49ab1e5cf35480ba46...05b42c624433fc40578a4040d5cf5e36ddca8cde) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-version: 2.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 28c1ab261..2b0a9860b 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -39,7 +39,7 @@ jobs: with: persist-credentials: false - name: Run Analysis - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 with: results_file: results.sarif results_format: sarif From b93dbaa79f356788b9e3326599ba128378f12023 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:50:36 +0200 Subject: [PATCH 0722/1008] build(deps): bump org.apache.maven.plugins:maven-gpg-plugin (#1951) Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.7 to 3.2.8. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.7...maven-gpg-plugin-3.2.8) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-version: 3.2.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c7e850e32..3d9b01edd 100644 --- a/pom.xml +++ b/pom.xml @@ -107,7 +107,7 @@ <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> - <maven-gpg-plugin.version>3.2.7</maven-gpg-plugin.version> + <maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version> <junit.version>5.10.2</junit.version> <aws-embedded-metrics.version>4.1.2</aws-embedded-metrics.version> <jmespath.version>0.6.0</jmespath.version> From 552431692756384d084b1d6cebb14d92a9a28f83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:51:08 +0200 Subject: [PATCH 0723/1008] build(deps): bump com.amazonaws:aws-lambda-java-serialization (#1952) Bumps [com.amazonaws:aws-lambda-java-serialization](https://github.com/aws/aws-lambda-java-libs) from 1.1.5 to 1.1.6. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-serialization dependency-version: 1.1.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3d9b01edd..92f133cc8 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.0</lambda.events.version> - <lambda.serial.version>1.1.5</lambda.serial.version> + <lambda.serial.version>1.1.6</lambda.serial.version> <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> <aspectj-maven-plugin.version>1.13.1</aspectj-maven-plugin.version> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index d0636baf2..8cdee8b5d 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -37,7 +37,7 @@ <kafka-clients.version>4.0.0</kafka-clients.version> <avro.version>1.12.0</avro.version> <protobuf.version>4.31.0</protobuf.version> - <lambda-serialization.version>1.1.5</lambda-serialization.version> + <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> <dependencies> From a12aadc97423291fbfb822a4e31c8a3625c68869 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:51:49 +0200 Subject: [PATCH 0724/1008] chore: bump actions/upload-artifact from 4.5.0 to 4.6.2 (#1953) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.5.0...ea165f8d65b6e75b540449e92b4886f43607fa02) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 4.6.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc462fbfc..ae2a90885 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,7 +112,7 @@ jobs: snapshot: ${{ inputs.snapshot}} - id: upload_source name: Upload artifacts - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: if-no-files-found: error name: source diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 2b0a9860b..4f85a0322 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -46,7 +46,7 @@ jobs: publish_results: true repo_token: ${{ secrets.SCORECARD_TOKEN }} - name: Upload Results - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: SARIF file path: results.sarif From bc2d67971763a647c1188db436542ab514ec94be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:55:40 +0200 Subject: [PATCH 0725/1008] chore: bump dependabot/fetch-metadata from 2.3.0 to 2.4.0 (#1954) Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.3.0 to 2.4.0. - [Release notes](https://github.com/dependabot/fetch-metadata/releases) - [Commits](https://github.com/dependabot/fetch-metadata/compare/d7267f607e9d3fb96fc2fbe83e0af444713e90b7...08eff52bf64351f401fb50d4972fa95b9f2c2d1b) --- updated-dependencies: - dependency-name: dependabot/fetch-metadata dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependabot.yml b/.github/workflows/security-dependabot.yml index e1422fb2b..3baa20897 100644 --- a/.github/workflows/security-dependabot.yml +++ b/.github/workflows/security-dependabot.yml @@ -26,7 +26,7 @@ jobs: steps: - id: dependabot-metadata name: Fetch Dependabot metadata - uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0 + uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0 - name: Fail workflow if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' }} run: | From ad285d6b7e03d2a20ed15672a432f4f1cbd8729b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:58:08 +0200 Subject: [PATCH 0726/1008] build(deps): bump org.graalvm.buildtools:native-maven-plugin (#1956) Bumps [org.graalvm.buildtools:native-maven-plugin](https://github.com/graalvm/native-build-tools) from 0.10.1 to 0.11.0. - [Release notes](https://github.com/graalvm/native-build-tools/releases) - [Commits](https://github.com/graalvm/native-build-tools/compare/0.10.1...0.11.0) --- updated-dependencies: - dependency-name: org.graalvm.buildtools:native-maven-plugin dependency-version: 0.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-parameters/powertools-parameters-appconfig/pom.xml | 2 +- powertools-parameters/powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 6fbe62ec2..4c174fe06 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -158,7 +158,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.1</version> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 3336f6bd1..337e0101d 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -176,7 +176,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.1</version> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 2239515f3..6320e8714 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -61,7 +61,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.1</version> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 27e2dbfb4..891a6380d 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -129,7 +129,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index fe32aa534..3f9748028 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -155,7 +155,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index fd081b963..50a569ea9 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -134,7 +134,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 6dc6167ab..1e8d6c1f4 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -124,7 +124,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 18b11f5c3..ba4084971 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -153,7 +153,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.6</version> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 2429d374c..ca4f1b46e 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -132,7 +132,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 825e50d90..554729e51 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -121,7 +121,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index b1bfaa5df..32f5d999a 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -122,7 +122,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 34031ca42..3aec19c87 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -122,7 +122,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index faecdf157..70d39a7ef 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -136,7 +136,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 55a3b9509..ec6190efa 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -132,7 +132,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e0867b486..a496c5245 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -129,7 +129,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 4d8326d6b..13c6db97e 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -152,7 +152,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.2</version> <!-- or newer version --> + <version>0.11.0</version> <!-- or newer version --> <extensions>true</extensions> <executions> <execution> From 703e79fa447f413b731e5b9275072a003e547ac0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:59:29 +0200 Subject: [PATCH 0727/1008] chore: bump actions/setup-java from 3.11.0 to 4.7.1 (#1957) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3.11.0 to 4.7.1. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3.11.0...c5195efecf7bdfc987ee8bae7a71cb8b11521c00) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 4.7.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index b5fe372dc..45fef40b9 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -96,7 +96,7 @@ jobs: name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: distribution: corretto java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 19500b2e5..345ea7548 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -58,7 +58,7 @@ jobs: steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'corretto' java-version: ${{ matrix.java }} @@ -91,7 +91,7 @@ jobs: steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'corretto' java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index d97698af8..7ef10c1de 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: java-version: 21 distribution: corretto diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index fd76d9560..9f3333187 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup Java - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'corretto' java-version: 21 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae2a90885..9f3232bb6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,7 +137,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: distribution: corretto java-version: 21 @@ -172,7 +172,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: distribution: corretto java-version: ${{ matrix.java }} @@ -195,7 +195,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: distribution: corretto java-version: 21 From 79934fd49279c159f425404687bf67e9c4e8074e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:59:58 +0200 Subject: [PATCH 0728/1008] chore: bump github/codeql-action from 3.27.9 to 3.29.3 (#1958) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.9 to 3.29.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/df409f7d9260372bd5f19e5b04e83cb3c43714ae...d6bbdef45e766d081b84a2def353b0055f728d3e) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 4f85a0322..d800f9379 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 + uses: github/codeql-action/upload-sarif@d6bbdef45e766d081b84a2def353b0055f728d3e # v3.29.3 with: sarif_file: results.sarif From 1a09a7ddb4b2513364a3fc139bf84641c857a460 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:00:28 +0200 Subject: [PATCH 0729/1008] build(deps): bump com.google.protobuf:protobuf-java (#1959) Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 4.31.0 to 4.31.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v4.31.0...v4.31.1) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-version: 4.31.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 4ea02a649..9e1034780 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -12,7 +12,7 @@ <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.31.0</protobuf.version> + <protobuf.version>4.31.1</protobuf.version> </properties> <dependencies> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 8cdee8b5d..454c07686 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -36,7 +36,7 @@ <properties> <kafka-clients.version>4.0.0</kafka-clients.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.31.0</protobuf.version> + <protobuf.version>4.31.1</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> From f8cc952b31e8b7a10a9010f9fcaad1caef5552d6 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 22 Jul 2025 10:48:08 +0200 Subject: [PATCH 0730/1008] chore(ci): Add Docker paths via globs to dependabot and update Dockerfiles to pin sha256 (#1960) * Add Docker paths to dependabot and update Dockerfiles to pin image version. * Remove dependabot auto-merging workflow. --- .github/dependabot.yml | 8 ++-- .github/workflows/security-dependabot.yml | 42 ------------------- .../sam-graalvm/Dockerfile | 2 +- .../sam-graalvm/Dockerfile | 2 +- .../sam-graalvm/Dockerfile | 2 +- 5 files changed, 8 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/security-dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c2baff44e..caa9934ca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,11 +9,13 @@ updates: - package-ecosystem: docker directories: - - "/powertools-e2e-tests" - - "/examples" - labels: [ ] + - "/powertools-e2e-tests/src/test/resources/docker" + - "/docs" + - "/examples/**" schedule: interval: daily + commit-message: + prefix: chore - package-ecosystem: "maven" directory: "/" diff --git a/.github/workflows/security-dependabot.yml b/.github/workflows/security-dependabot.yml deleted file mode 100644 index 3baa20897..000000000 --- a/.github/workflows/security-dependabot.yml +++ /dev/null @@ -1,42 +0,0 @@ -# Auto merges dependabot PRs -# -# Description: -# Auto-merges dependabot PRs if all checks pass -# We verify all commits in the PR to ensure no one else has committed to the PR -# -# Triggers: -# - pull_request - -on: - pull_request: - branches: [ dependabot/* ] - -name: Dependabot updates -run-name: Dependabot - -permissions: - contents: read - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'aws-powertools/powertools-lambda-java' }} - permissions: - pull-requests: write - steps: - - id: dependabot-metadata - name: Fetch Dependabot metadata - uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0 - - name: Fail workflow - if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' }} - run: | - echo "::error::Major version upgrades are not wanted" - - name: Approve PR - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh pr review "${{ github.event.pull_request.html_url }}" --approve --body '🤖 Approved by another robot.' - - name: Enable auto-merge on PR - run: gh pr merge --auto --squash "${{ github.event.pull_request.html_url }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile b/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile index a690606ad..8377d6dc7 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile +++ b/examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile @@ -1,5 +1,5 @@ #Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21:latest +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 diff --git a/examples/powertools-examples-parameters/sam-graalvm/Dockerfile b/examples/powertools-examples-parameters/sam-graalvm/Dockerfile index a690606ad..8377d6dc7 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/Dockerfile +++ b/examples/powertools-examples-parameters/sam-graalvm/Dockerfile @@ -1,5 +1,5 @@ #Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21:latest +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 diff --git a/examples/powertools-examples-serialization/sam-graalvm/Dockerfile b/examples/powertools-examples-serialization/sam-graalvm/Dockerfile index a690606ad..8377d6dc7 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/Dockerfile +++ b/examples/powertools-examples-serialization/sam-graalvm/Dockerfile @@ -1,5 +1,5 @@ #Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21:latest +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 From 429cc5b61c354382661b4b2f4be3d7e2750445c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:00:46 +0200 Subject: [PATCH 0731/1008] chore: bump squidfunk/mkdocs-material in /docs (#1961) Bumps squidfunk/mkdocs-material from `23b6978` to `0bfdba4`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 0bfdba448e93984191246f7a28abeacc79f789e7e9cf0c639a48fe4365e880a7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 8b5e5e275..d4f33c224 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:23b69789b1dd836c53ea25b32f62ef8e1a23366037acd07c90959a219fd1f285 +FROM squidfunk/mkdocs-material@sha256:0bfdba448e93984191246f7a28abeacc79f789e7e9cf0c639a48fe4365e880a7 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 78237a1d9f8c6bc364c957530ef86d4dd7b0567c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:08:01 +0200 Subject: [PATCH 0732/1008] chore: bump sam/build-java21 (#1962) Bumps sam/build-java21 from `a5554d6` to `44e9166`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 44e9166767b12df862da6f76b4622f89e3b97a31bab73bfbb3b1df13515bfd23 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index a244dd60d..927a221c4 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461 +FROM public.ecr.aws/sam/build-java21@sha256:44e9166767b12df862da6f76b4622f89e3b97a31bab73bfbb3b1df13515bfd23 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 47a41c71dadd74739663c5267ff41f5bb03927f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:08:26 +0200 Subject: [PATCH 0733/1008] chore: bump actions/checkout from 3.5.3 to 4.2.2 (#1963) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 4.2.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.5.3...11bd71901bbe5b1630ceea73d27597364c9af683) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 4.2.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-spotbugs.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 345ea7548..a50bc8838 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -56,7 +56,7 @@ jobs: - 21 steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: @@ -89,7 +89,7 @@ jobs: - 21 steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index 9f3333187..754672448 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -40,7 +40,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: From 79b21fcf9f13d97b1a8a3cc7cf553a8add131d40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:08:51 +0200 Subject: [PATCH 0734/1008] build(deps): bump slf4j.version from 2.0.7 to 2.0.17 (#1964) Bumps `slf4j.version` from 2.0.7 to 2.0.17. Updates `org.slf4j:slf4j-api` from 2.0.7 to 2.0.17 Updates `org.slf4j:slf4j-simple` from 2.0.7 to 2.0.17 --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-version: 2.0.17 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-version: 2.0.17 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 92f133cc8..a7a867339 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ <maven.compiler.target>11</maven.compiler.target> <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.25.1</log4j.version> - <slf4j.version>2.0.7</slf4j.version> + <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.1</jackson.version> <aws.sdk.version>2.31.78</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 09589595f..992f6d0e1 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -60,7 +60,7 @@ <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> - <version>2.0.7</version> + <version>2.0.17</version> <scope>test</scope> </dependency> <dependency> From 560bbaa98dc1ecf38e97148d4125c900736f924b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:21:28 +0200 Subject: [PATCH 0735/1008] chore: bump actions/dependency-review-action from 4.5.0 to 4.7.1 (#1968) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.5.0 to 4.7.1. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/3b139cfc5fae8b618d3eae3675e383bb1769c019...da24556b548a50705dd671f47852072ea4c105d9) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.7.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 9c588d9be..108ba26fe 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -34,6 +34,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Verify Contents - uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 + uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 with: config-file: './.github/dependency-review-config.yml' \ No newline at end of file From 567dc6bce3dbfa02fdc0a766ac72106c29def735 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:27:33 +0200 Subject: [PATCH 0736/1008] chore: bump aws-actions/configure-aws-credentials from 2.2.0 to 4.2.1 (#1965) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 2.2.0 to 4.2.1. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/v2.2.0...b47578312673ae6fa5b5096b330d9fbac3d116df) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-version: 4.2.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 5d6870171..f9cbfe1ea 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -41,7 +41,7 @@ jobs: 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@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index a50bc8838..020b89b2a 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -64,7 +64,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 @@ -97,7 +97,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f3232bb6..988f7fb79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -278,7 +278,7 @@ jobs: 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@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} From 07ecd3ce19ba6937f323b0d0e810f64858d877d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:37:17 +0200 Subject: [PATCH 0737/1008] build(deps): bump commons-io:commons-io from 2.19.0 to 2.20.0 (#1966) Bumps [commons-io:commons-io](https://github.com/apache/commons-io) from 2.19.0 to 2.20.0. - [Changelog](https://github.com/apache/commons-io/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-io/compare/rel/commons-io-2.19.0...rel/commons-io-2.20.0) --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 3bbaca3e2..eed632ea0 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -103,7 +103,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.19.0</version> + <version>2.20.0</version> </dependency> <dependency> From f4421d81099aac8753198a254f863ba3455bcda8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:37:44 +0200 Subject: [PATCH 0738/1008] chore: bump actions/download-artifact from 4.2.1 to 4.3.0 (#1967) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.2.1 to 4.3.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/95815c38cf2ff2164869cbab79da8d1f422bc89e...d3f86a106a0bac45b974a628896c90dbdf5c8093) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 4.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 988f7fb79..a56301591 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 with: name: source - name: Setup Java @@ -168,7 +168,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 with: name: source - name: Setup Java @@ -191,7 +191,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 with: name: source - name: Setup Java @@ -229,7 +229,7 @@ jobs: ref: ${{ env.RELEASE_COMMIT }} - id: download_source name: Download artifacts - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.6.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 with: name: source - id: setup-git From dc8692743fe9122040d39b8d8c4e2255c91a09c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:38:15 +0200 Subject: [PATCH 0739/1008] build(deps): bump jackson.version from 2.19.1 to 2.19.2 (#1969) Bumps `jackson.version` from 2.19.1 to 2.19.2. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.1...jackson-core-2.19.2) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.19.1 to 2.19.2 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Leandro Damascena <lcdama@amazon.pt> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7a867339..d3e871b7e 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> - <jackson.version>2.19.1</jackson.version> + <jackson.version>2.19.2</jackson.version> <aws.sdk.version>2.31.78</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> From c4ac6619f05ac18c4247bd91265acee310ca8ff4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:39:33 +0200 Subject: [PATCH 0740/1008] build(deps): bump mockito.version from 4.11.0 to 5.18.0 (#1970) Bumps `mockito.version` from 4.11.0 to 5.18.0. Updates `org.mockito:mockito-core` from 4.11.0 to 5.18.0 - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.11.0...v5.18.0) Updates `org.mockito:mockito-subclass` from 5.6.0 to 5.18.0 - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.6.0...v5.18.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-version: 5.18.0 dependency-type: direct:production update-type: version-update:semver-major - dependency-name: org.mockito:mockito-subclass dependency-version: 5.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- powertools-common/pom.xml | 4 ++-- powertools-logging/pom.xml | 4 ++-- powertools-logging/powertools-logging-log4j/pom.xml | 4 ++-- powertools-logging/powertools-logging-logback/pom.xml | 4 ++-- powertools-parameters/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-appconfig/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-dynamodb/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-secrets/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-ssm/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-tests/pom.xml | 4 ++-- powertools-serialization/pom.xml | 4 ++-- powertools-tracing/pom.xml | 4 ++-- 16 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index a56eae46d..010d313fa 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -63,7 +63,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>4.11.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> <dependency> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 337e0101d..95887d566 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -76,7 +76,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>5.1.1</version> + <version>5.18.0</version> <scope>test</scope> </dependency> <dependency> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index c729ed83c..45c71e3ed 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -50,7 +50,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>5.1.1</version> + <version>5.18.0</version> <scope>test</scope> </dependency> <dependency> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 6955fc4ea..153a2f77f 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -53,7 +53,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>4.11.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 891a6380d..37039f51b 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -94,7 +94,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -120,7 +120,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 3f9748028..b86935998 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -120,7 +120,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -146,7 +146,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 50a569ea9..6b1212d45 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -99,7 +99,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -125,7 +125,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 1e8d6c1f4..6adbde9de 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -92,7 +92,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -115,7 +115,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index ca4f1b46e..c8ae5fe34 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -97,7 +97,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -123,7 +123,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 554729e51..823d99698 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -86,7 +86,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -112,7 +112,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 32f5d999a..1194418e9 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -87,7 +87,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -113,7 +113,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 3aec19c87..12d72b58b 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -87,7 +87,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -113,7 +113,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 70d39a7ef..1701140eb 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -101,7 +101,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -127,7 +127,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index ec6190efa..a4ff090ef 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -97,7 +97,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -123,7 +123,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.6.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index a496c5245..56e639ced 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -97,7 +97,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -120,7 +120,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 13c6db97e..7b430f869 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -117,7 +117,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -143,7 +143,7 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.17.0</version> + <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> From c08e1ef92e9c342bd247909a6b9a6bd158bb5a9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:41:53 +0200 Subject: [PATCH 0741/1008] chore: bump org.apache.maven.plugins:maven-compiler-plugin (#1972) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.13.0 to 3.14.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index d6a4dc3cf..d15907cd3 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -16,7 +16,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.13.0</version> + <version>3.14.0</version> <configuration> <source>11</source> <target>11</target> diff --git a/pom.xml b/pom.xml index d3e871b7e..9c353dbc4 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.0</lambda.events.version> <lambda.serial.version>1.1.6</lambda.serial.version> - <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version> + <maven-compiler-plugin.version>3.14.0</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.2.5</maven-surefire-plugin.version> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index eed632ea0..7a65c04e5 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -194,7 +194,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.13.0</version> + <version>3.14.0</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From 9f239f5a05521ed7dbf07d4cc729df3aa21732d1 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 24 Jul 2025 14:37:47 +0200 Subject: [PATCH 0742/1008] chore(ci): Remove osv workflow. (#1973) Co-authored-by: Leandro Damascena <lcdama@amazon.pt> --- .github/workflows/security-osv.yml | 35 ------------------------------ 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/security-osv.yml diff --git a/.github/workflows/security-osv.yml b/.github/workflows/security-osv.yml deleted file mode 100644 index 73d6b1835..000000000 --- a/.github/workflows/security-osv.yml +++ /dev/null @@ -1,35 +0,0 @@ -# Runs OSV scan -# -# Description: -# Checks dependencies already in the project for known issues -# -# Triggers: -# - pull_request -# - workflow_dispatch -# - cron -# - push - -on: - pull_request: - branches: - - main - workflow_dispatch: {} - schedule: - - cron: "30 12 * * 1" - push: - branches: - - main - -name: OpenSource Vulnerability Scanner -run-name: OpenSource Vulnerability Scanner - -permissions: - contents: read - -jobs: - scan-pr: - permissions: - actions: read - contents: read - security-events: write - uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b00f71e051ddddc6e46a193c31c8c0bf283bf9e6 # v2.1.0 From 2ebd14225047b8c4d7e8f8a374b9232219e3c05c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:39:13 +0200 Subject: [PATCH 0743/1008] chore: bump com.networknt:json-schema-validator from 1.5.1 to 1.5.8 (#1974) Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.5.1 to 1.5.8. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.1...1.5.8) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-version: 1.5.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index b51b531fa..73a150917 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -65,7 +65,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.5.1</version> + <version>1.5.8</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From 3fa2b455e7c13ee166d35abc7405d6d2d0f0b379 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 15:02:01 +0200 Subject: [PATCH 0744/1008] build(deps): bump aws.sdk.version from 2.31.78 to 2.32.5 (#1971) Bumps `aws.sdk.version` from 2.31.78 to 2.32.5. Updates `software.amazon.awssdk:bom` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:http-client-spi` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:url-connection-client` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:dynamodb` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:s3` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:lambda` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:kinesis` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:cloudwatch` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:xray` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:sqs` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:cloudformation` from 2.31.78 to 2.32.5 Updates `software.amazon.awssdk:sts` from 2.31.78 to 2.32.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.5 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.5 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.5 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d6c2dbca0..1f0771eff 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.0</lambda.events.version> - <aws.sdk.version>2.31.78</aws.sdk.version> + <aws.sdk.version>2.32.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 9c353dbc4..45fdcdb29 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.31.78</aws.sdk.version> + <aws.sdk.version>2.32.5</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 2bb51d8064919ac4795c8ab8b6fb603fc2c65530 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 29 Jul 2025 09:59:19 +0200 Subject: [PATCH 0745/1008] fix(parameters): Correctly check for empty values in AppConfig Parameters Provider. (#1982) * fix(parameters): Correctly check for empty values in AppConfig Parameters Provider. * fix pmd findings. --- .../appconfig/AppConfigProvider.java | 44 +++++----- .../appconfig/AppConfigProviderTest.java | 84 ++++++++++--------- 2 files changed, 66 insertions(+), 62 deletions(-) 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 index 5fd272c9b..37f07ae7a 100644 --- 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 @@ -16,6 +16,8 @@ import java.util.HashMap; import java.util.Map; + +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; @@ -44,10 +46,10 @@ 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<>(); + private final Map<String, EstablishedSession> establishedSessions = new HashMap<>(); AppConfigProvider(CacheManager cacheManager, TransformationManager transformationManager, - AppConfigDataClient client, String environment, String application) { + AppConfigDataClient client, String environment, String application) { super(cacheManager, transformationManager); this.client = client; this.application = application; @@ -63,7 +65,6 @@ public static AppConfigProviderBuilder builder() { return new AppConfigProviderBuilder(); } - /** * Retrieve the parameter value from the AppConfig parameter store.<br /> * @@ -76,13 +77,12 @@ protected String getValue(String key) { // 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()) + 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 @@ -93,16 +93,18 @@ protected String getValue(String key) { // 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 - + // 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)); @@ -116,7 +118,7 @@ protected Map<String, String> getMultipleValues(String path) { "Retrieving multiple parameter values is not supported with the AWS App Config Provider"); } - private static class EstablishedSession { + private static final class EstablishedSession { private final String nextSessionToken; private final String lastConfigurationValue; diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java index 039611875..da01d7d9a 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java @@ -14,9 +14,9 @@ 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.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.MockitoAnnotations.openMocks; import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; @@ -27,6 +27,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; + import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest; @@ -36,11 +37,11 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class AppConfigProviderTest { +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; @@ -53,19 +54,18 @@ public class AppConfigProviderTest { private AppConfigProvider provider; @BeforeEach - public void init() { + void init() { openMocks(this); 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 @@ -73,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") @@ -92,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( @@ -119,8 +127,7 @@ public void getValueRetrievesValue() { } @Test - public void getValueNoValueExists() { - + void getValueNoValueExists() { // Arrange StartConfigurationSessionResponse session = StartConfigurationSessionResponse.builder() .initialConfigurationToken("token1") @@ -134,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(); } /** @@ -146,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() @@ -184,49 +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 testAppConfigProviderBuilderMissingEnvironment_throwsException() { - + void testAppConfigProviderBuilderMissingEnvironment_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() - .withCacheManager(new CacheManager()) - .withApplication(applicationName) - .withClient(client) - .build()) + .withCacheManager(new CacheManager()) + .withApplication(APPLICATION_NAME) + .withClient(client) + .build()) .withMessage("No environment provided; please provide one"); } @Test - public void testAppConfigProviderBuilderMissingApplication_throwsException() { - + void testAppConfigProviderBuilderMissingApplication_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> AppConfigProvider.builder() - .withCacheManager(new CacheManager()) - .withEnvironment(environmentName) - .withClient(client) - .build()) + .withCacheManager(new CacheManager()) + .withEnvironment(ENVIRONMENT_NAME) + .withClient(client) + .build()) .withMessage("No application provided; please provide one"); } - @Test - public void testAppConfigProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + @Test + void testAppConfigProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act AppConfigProvider appConfigProvider = AppConfigProvider.builder() .withEnvironment("test") .withApplication("app") .build(); // Assert - assertDoesNotThrow(()->appConfigProvider.withTransformation(json)); + assertDoesNotThrow(() -> appConfigProvider.withTransformation(json)); } } From 9728cf47b74076e7fef0da459c8c95b61e6b2518 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:16:03 +0200 Subject: [PATCH 0746/1008] chore: bump com.amazonaws:aws-lambda-java-events from 3.16.0 to 3.16.1 (#1975) Bumps [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs) from 3.16.0 to 3.16.1. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-events dependency-version: 3.16.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 1f0771eff..d8b01aa41 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -13,7 +13,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> - <lambda.events.version>3.16.0</lambda.events.version> + <lambda.events.version>3.16.1</lambda.events.version> <aws.sdk.version>2.32.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 1f0f9f4ef..1d40289d9 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 4c174fe06..468b9ac3b 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index ba436c650..dd675ae65 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index b62f193aa..6f92663c4 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index a09b26ac6..74d2cd78b 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 010d313fa..2eb307f8b 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -52,7 +52,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 95887d566..cd689787f 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 45c71e3ed..978e0313e 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -38,7 +38,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 6320e8714..d34d168c9 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index ed70081a2..39673f15f 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -31,7 +31,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> - <version>3.16.0</version> + <version>3.16.1</version> </dependency> </dependencies> diff --git a/pom.xml b/pom.xml index 45fdcdb29..bc72893ad 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <lambda.core.version>1.3.0</lambda.core.version> - <lambda.events.version>3.16.0</lambda.events.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.0</maven-compiler-plugin.version> <aspectj.version>1.9.7</aspectj.version> From 100c146ad8a9ad66ffeabd6ad3ab4fba46a2b7c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:16:41 +0200 Subject: [PATCH 0747/1008] chore: bump aws.sdk.version from 2.31.78 to 2.32.6 (#1976) Bumps `aws.sdk.version` from 2.31.78 to 2.32.6. Updates `software.amazon.awssdk:url-connection-client` from 2.31.78 to 2.32.6 Updates `software.amazon.awssdk:sdk-core` from 2.31.78 to 2.32.6 Updates `software.amazon.awssdk:s3` from 2.31.78 to 2.32.6 Updates `software.amazon.awssdk:kinesis` from 2.31.78 to 2.32.6 Updates `software.amazon.awssdk:sqs` from 2.31.78 to 2.32.6 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.31.78 to 2.32.6 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.32.6 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.32.6 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index abc4d660b..7db5a9688 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.31.78</sdk.version> + <sdk.version>2.32.6</sdk.version> </properties> <dependencies> From 0f2420c5de272b6a33634a835ef64ffac747a904 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:17:12 +0200 Subject: [PATCH 0748/1008] chore: bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions (#1977) Bumps org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions from 0.1.0 to 0.2.0. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions dependency-version: 0.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 468b9ac3b..acc8b55e4 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -135,7 +135,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index cd689787f..98dfb6d82 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -153,7 +153,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> From f050d35b15069cb7756402ff646cc639108960ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:17:34 +0200 Subject: [PATCH 0749/1008] chore: bump github/codeql-action from 3.29.3 to 3.29.4 (#1978) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.3 to 3.29.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/d6bbdef45e766d081b84a2def353b0055f728d3e...4e828ff8d448a8a6e532957b1811f387a63867e8) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index d800f9379..1d88a8b9f 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@d6bbdef45e766d081b84a2def353b0055f728d3e # v3.29.3 + uses: github/codeql-action/upload-sarif@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: sarif_file: results.sarif From ad36ab3f0fd1deb5da9af008494f1771401da7cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:17:53 +0200 Subject: [PATCH 0750/1008] build(deps): bump com.amazonaws:aws-lambda-java-tests (#1979) Bumps [com.amazonaws:aws-lambda-java-tests](https://github.com/aws/aws-lambda-java-libs) from 1.1.1 to 1.1.2. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-tests dependency-version: 1.1.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bc72893ad..1a1d2ea58 100644 --- a/pom.xml +++ b/pom.xml @@ -370,7 +370,7 @@ <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> From aa864c90db62aaaea83538002275cbf76dd50480 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:18:22 +0200 Subject: [PATCH 0751/1008] build(deps): bump io.github.ascopes:protobuf-maven-plugin (#1980) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.3.0 to 3.6.1. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.3.0...v3.6.1) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 9e1034780..b962c0136 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.3.0</version> + <version>3.6.1</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 454c07686..3056cc33d 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -181,7 +181,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.3.0</version> + <version>3.6.1</version> <executions> <execution> <id>generate-test-sources</id> From c70a9dd0c256e101e2d8492555607bc493037581 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:54:24 +0200 Subject: [PATCH 0752/1008] chore(ci): bump version to 2.2.1 (#1983) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 44 files changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 3ea636922..d5f240b6e 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 4bea96700..bbf2f991e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 7db5a9688..0d288f8ba 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index f0a15f1cd..98302aab9 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-20230718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.2.1718 ``` ### Deploy with CDK @@ -32,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.2.1718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index d8b01aa41..96f9c0337 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 1d40289d9..67705c89e 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index d15907cd3..0a2949658 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.162.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 4cee5ecfa..124c7b60b 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.2.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.2.0' - aspect 'software.amazon.lambda:powertools-metrics:2.2.0' + aspect 'software.amazon.lambda:powertools-tracing:2.2.1' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.2.1' + aspect 'software.amazon.lambda:powertools-metrics:2.2.1' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index e769c3c45..25ff24fac 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.2.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.2.0") - aspect("software.amazon.lambda:powertools-metrics:2.2.0") + aspect("software.amazon.lambda:powertools-tracing:2.2.1") + aspect("software.amazon.lambda:powertools-logging-log4j:2.2.1") + aspect("software.amazon.lambda:powertools-metrics:2.2.1") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index acc8b55e4..d0fb3b8ea 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index dd675ae65..0208798fc 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 6f92663c4..a715553fb 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 74d2cd78b..c80a9566d 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 2eb307f8b..96f5002fe 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index b962c0136..7c87a964b 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 98dfb6d82..fcdf1ffef 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 978e0313e..129095534 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index d34d168c9..a67433a08 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 39673f15f..78bb6caad 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 153a2f77f..1227acc42 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.2.0</version> + <version>2.2.1</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 9b70afcb3..e258d8c5a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,7 +121,7 @@ extra_javascript: extra: powertools: - version: 2.2.0 + version: 2.2.1 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 1a1d2ea58..4a8dcf0b9 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 992f6d0e1..74eae5726 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index a5888bf5d..b4d247178 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 37039f51b..adc86547d 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 7a65c04e5..38cd6a135 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index f40bc7ee6..08eefc473 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index c8f1e58d6..0f2c2dec1 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 701259da3..3c3304bf9 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 3056cc33d..0e1a85b6d 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 6b9b258ca..c9bfb2476 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index b86935998..e6b9d6603 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 6b1212d45..836ded5ed 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 6adbde9de..1ad566809 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index ba4084971..b91789b4b 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index c8ae5fe34..1bc7efb52 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 823d99698..06567c284 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 1194418e9..39b1bf083 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 12d72b58b..6d156e13c 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 1701140eb..3b7916e3e 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index a4ff090ef..b83793200 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.0</version> + <version>2.2.1</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 56e639ced..9905d0f82 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 7b430f869..2fa82e708 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 73a150917..b845bdb20 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.0</version> + <version>2.2.1</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From b84c531e5247688d540772b37d63c3a3a1142ccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:50:54 +0200 Subject: [PATCH 0753/1008] chore: bump squidfunk/mkdocs-material in /docs (#1984) Bumps squidfunk/mkdocs-material from `0bfdba4` to `bb7b015`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: bb7b015690d9fb5ef0dbc98ca3520f153aa43129fb96aec5ca54c9154dc3b729 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index d4f33c224..858c60705 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:0bfdba448e93984191246f7a28abeacc79f789e7e9cf0c639a48fe4365e880a7 +FROM squidfunk/mkdocs-material@sha256:bb7b015690d9fb5ef0dbc98ca3520f153aa43129fb96aec5ca54c9154dc3b729 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From adc0a31acbed0215e257b12ab68484f2ca1f04ec Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 30 Jul 2025 18:26:50 +0200 Subject: [PATCH 0754/1008] docs(examples): Enable end to end tracing for SQS batch example. (#1995) --- .../deploy/sqs/template.yml | 2 +- .../demo/batch/sqs/AbstractSqsBatchHandler.java | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-batch/deploy/sqs/template.yml b/examples/powertools-examples-batch/deploy/sqs/template.yml index 2f1d6c363..1232e4d51 100644 --- a/examples/powertools-examples-batch/deploy/sqs/template.yml +++ b/examples/powertools-examples-batch/deploy/sqs/template.yml @@ -65,6 +65,7 @@ Resources: DemoSQSSenderFunction: Type: AWS::Serverless::Function Properties: + Tracing: Active CodeUri: ../.. Handler: org.demo.batch.sqs.SqsBatchSender::handleRequest Environment: @@ -173,7 +174,6 @@ Resources: Action: - s3:PutObject Resource: !Sub ${Bucket.Arn}/* - Events: MySQSEvent: Type: SQS 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 index ee33b50fd..1b3029d40 100644 --- 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 @@ -14,21 +14,22 @@ package org.demo.batch.sqs; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.ObjectMapper; 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.logging.Logging; import software.amazon.lambda.powertools.tracing.Tracing; import software.amazon.lambda.powertools.tracing.TracingUtils; @@ -43,7 +44,6 @@ public class AbstractSqsBatchHandler { * Simulate some processing (I/O + S3 put request) * @param p deserialized product */ - @Logging @Tracing protected void processMessage(Product p) { TracingUtils.putAnnotation("productId", p.getId()); @@ -51,15 +51,16 @@ protected void processMessage(Product p) { MDC.put("product", String.valueOf(p.getId())); LOGGER.info("Processing product {}", p); - char c = (char)(r.nextInt(26) + 'a'); + 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"); + 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)); + PutObjectRequest.builder().bucket(bucket).key(p.getId() + ".json").build(), + RequestBody.fromFile(file)); } catch (IOException e) { throw new RuntimeException(e); } finally { From 030564242729527e0c0ed2fd12750684aae75612 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 30 Jul 2025 18:27:02 +0200 Subject: [PATCH 0755/1008] docs(examples): Add Logging and Tracing to idempotency example with correct configuration. (#1993) --- .../kotlin/.gitignore | 1 + .../src/main/java/helloworld/App.java | 3 +++ .../src/main/resources/log4j2.xml | 16 ++++++++++++++++ .../org/demo/kafka/protobuf/ProtobufProduct.java | 4 ++-- .../kafka/protobuf/ProtobufProductOrBuilder.java | 2 +- .../protobuf/ProtobufProductOuterClass.java | 4 ++-- 6 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 examples/powertools-examples-core-utilities/kotlin/.gitignore create mode 100644 examples/powertools-examples-idempotency/src/main/resources/log4j2.xml 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-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index 029877c73..ebe9fac22 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -34,6 +34,7 @@ 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> { @@ -91,6 +92,7 @@ public App(DynamoDbClient client) { */ @Idempotent // The magic is here! @Logging(logEvent = true) + @Tracing public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); @@ -130,6 +132,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv * @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))) { diff --git a/examples/powertools-examples-idempotency/src/main/resources/log4j2.xml b/examples/powertools-examples-idempotency/src/main/resources/log4j2.xml new file mode 100644 index 000000000..5dede7b58 --- /dev/null +++ b/examples/powertools-examples-idempotency/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-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java index 6da9113fc..250f37090 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.31.0 +// Protobuf Java Version: 4.31.1 package org.demo.kafka.protobuf; @@ -19,7 +19,7 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 31, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", ProtobufProduct.class.getName()); } 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 index 9c1518db3..b2d185f6d 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.31.0 +// Protobuf Java Version: 4.31.1 package org.demo.kafka.protobuf; 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 index 6a99f35ec..adbfdd73a 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.31.0 +// Protobuf Java Version: 4.31.1 package org.demo.kafka.protobuf; @@ -13,7 +13,7 @@ private ProtobufProductOuterClass() {} com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 31, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", ProtobufProductOuterClass.class.getName()); } From 094c3d9e17e19b0003b1fa6719a59afc93f89f44 Mon Sep 17 00:00:00 2001 From: Subhash Kovela <46015546+subhash686@users.noreply.github.com> Date: Thu, 31 Jul 2025 11:02:51 -0400 Subject: [PATCH 0756/1008] feat: Support CRaC priming of powertools metrics and idempotency-dynamodb (#1861) * Automatic priming of powertools-metrics * Fixed resource name and added unit tests and javadoc comments * Invoke prime Dynamo persistent store * Fixed Sonar issues * Update classesloaded.txt replaced build time class list with run time class list * Moved priming to MetricsFactory and DynamoDBPersistenceStore, added Priming.md * Update classesloaded.txt classesloaded file generated using the new method * Update LambdaMetricsAspect.java Removed unused import * Update DynamoDBPersistenceStore.java Fixed variable naming Sonar issue * Improved priming.md documentation and static initialization of classes using for priming. Also fixed an unrelated flaky unit test * Fixed a Sonarqube finding and added more unit tests to ensure all available classes are loaded --------- Co-authored-by: Philipp Page <github@philipp.page> --- Priming.md | 59 + pom.xml | 6 + .../common/internal/ClassPreLoader.java | 91 + .../common/internal/ClassPreLoaderTest.java | 34 + .../src/test/resources/classesloaded.txt | 2 + .../powertools-idempotency-dynamodb/pom.xml | 4 + .../dynamodb/DynamoDBPersistenceStore.java | 38 +- .../internal/LambdaJsonEncoderTest.java | 2 +- powertools-metrics/pom.xml | 21 + .../powertools/metrics/MetricsFactory.java | 23 +- .../src/main/resources/classesloaded.txt | 3651 +++++++++++++++++ 11 files changed, 3927 insertions(+), 4 deletions(-) create mode 100644 Priming.md create mode 100644 powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java create mode 100644 powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java create mode 100644 powertools-common/src/test/resources/classesloaded.txt create mode 100644 powertools-metrics/src/main/resources/classesloaded.txt 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/pom.xml b/pom.xml index 4a8dcf0b9..db78120d1 100644 --- a/pom.xml +++ b/pom.xml @@ -117,6 +117,7 @@ <mockito.version>5.18.0</mockito.version> <mockito-junit-jupiter.version>5.18.0</mockito-junit-jupiter.version> <junit-pioneer.version>2.3.0</junit-pioneer.version> + <crac.version>1.4.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. --> @@ -264,6 +265,11 @@ <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> 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-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-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-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 3c3304bf9..d63719bbe 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -57,6 +57,10 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> <!-- Test dependencies --> <dependency> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java index 9c96541f6..c128fa5bd 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java @@ -14,6 +14,8 @@ package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; +import org.crac.Core; +import org.crac.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; @@ -37,6 +39,7 @@ 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; @@ -52,7 +55,7 @@ * 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); @@ -109,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() { 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 index 81e830045..c0eecf6f3 100644 --- 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 @@ -368,7 +368,7 @@ void shouldLogThreadInfo() { String result = new String(encoded, StandardCharsets.UTF_8); // THEN - assertThat(result).contains("\"thread\":\"main\",\"thread_id\":1,\"thread_priority\":5"); + assertThat(result).contains("\"thread\":\"main\",\"thread_id\":"+ Thread.currentThread().getId() +",\"thread_priority\":5"); } @Test diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index b91789b4b..7e59bdbf9 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -39,6 +39,10 @@ <artifactId>aspectjrt</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> @@ -114,6 +118,23 @@ </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> 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 index 1fd5f88ca..67ab17b7b 100644 --- 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 @@ -14,6 +14,9 @@ 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.model.DimensionSet; @@ -23,11 +26,16 @@ /** * Factory for accessing the singleton Metrics instance */ -public final class MetricsFactory { +public final class MetricsFactory implements Resource { private static MetricsProvider provider = new EmfMetricsProvider(); private static Metrics metrics; - private MetricsFactory() { + // 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); } /** @@ -68,4 +76,15 @@ public static synchronized void setMetricsProvider(MetricsProvider metricsProvid // Reset the metrics instance so it will be recreated with the new provider metrics = 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/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 From 07ff3b265bc1359b9195ed7845f46c890d2073f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:46:32 +0200 Subject: [PATCH 0757/1008] build(deps): bump aws.sdk.version from 2.32.5 to 2.32.10 (#1985) Bumps `aws.sdk.version` from 2.32.5 to 2.32.10. Updates `software.amazon.awssdk:bom` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:http-client-spi` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:url-connection-client` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:dynamodb` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:s3` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:lambda` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:kinesis` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:cloudwatch` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:xray` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:sqs` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:cloudformation` from 2.32.5 to 2.32.10 Updates `software.amazon.awssdk:sts` from 2.32.5 to 2.32.10 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.10 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.10 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.10 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 96f9c0337..619ccfcba 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.5</aws.sdk.version> + <aws.sdk.version>2.32.10</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index db78120d1..506df4232 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.5</aws.sdk.version> + <aws.sdk.version>2.32.10</aws.sdk.version> <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 22c35eddce945dd041e32daa32f0dd9dff9ca7d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:48:33 +0200 Subject: [PATCH 0758/1008] build(deps): bump org.apache.maven.plugins:maven-source-plugin (#1987) Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.0...maven-source-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-version: 3.3.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 506df4232..47db4dea9 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> - <maven-source-plugin.version>3.3.0</maven-source-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.10.2</junit.version> <aws-embedded-metrics.version>4.1.2</aws-embedded-metrics.version> From f0b6eed1099fee66514e5a5a3370255e332cdc3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:52:54 +0200 Subject: [PATCH 0759/1008] build(deps): bump org.junit:junit-bom from 5.10.2 to 5.13.4 (#1988) Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 5.10.2 to 5.13.4. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.10.2...r5.13.4) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-version: 5.13.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47db4dea9..a1b714540 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ <maven-javadoc-plugin.version>3.11.2</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.10.2</junit.version> + <junit.version>5.13.4</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> From 3798e63d62018179e608dd080dd292d36ca34acd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:53:36 +0200 Subject: [PATCH 0760/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.162.1 to 2.208.0 (#1990) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.162.1 to 2.208.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/v2.208.0/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.162.1...v2.208.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.208.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 0a2949658..317277af9 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.2.1</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.162.1</cdk.version> + <cdk.version>2.208.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.11.1</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 38cd6a135..1b36d4701 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.186.0</cdk.version> + <cdk.version>2.208.0</cdk.version> </properties> <dependencies> From e6c1676dceb21a4baf1130754aae731ffd41c6c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:53:58 +0200 Subject: [PATCH 0761/1008] chore: bump github/codeql-action from 3.29.4 to 3.29.5 (#1992) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.4 to 3.29.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4e828ff8d448a8a6e532957b1811f387a63867e8...51f77329afa6477de8c49fc9c7046c15b9a4e79d) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 1d88a8b9f..6367d2452 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 + uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 with: sarif_file: results.sarif From a691a3bde77b6b0a06884a577bc139290dc56256 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 6 Aug 2025 10:44:11 +0200 Subject: [PATCH 0762/1008] chore(ci): Make E2E tests compatible with latest CDK lib version. Improve retry implementation. (#2008) * chore(ci): Make E2E asset deployment compatible with updated CDK lib version. Refactor retry4j to resilience4j. * Add Retry logic to tests instead of Thread.sleep where appropriate. * Avoid request building code duplication in BatchE2ET.java. * Include TracingE2ET again after updating retry logic. * Address Sonarlint issues. * Downgrade resilience4j to Java11 compatible version 1.x * Fix pmd_analyze issues. * Fix more pmd_analyze issues. * Fix more pmd_analyze issues. * Fix more pmd_analyze issues. * Fix more pmd_analyze issues. * Fix more pmd_analyze issues. * Add GraalVM support for TracingE2ET. * Increase TracingE2ET timeout to 15 minutes. --- .../lambda/powertools/e2e/Function.java | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 24 +++- .../aws-lambda-java-core/reflect-config.json | 13 ++ .../reflect-config.json | 35 +++++ .../jni-config.json | 11 ++ .../native-image.properties | 1 + .../reflect-config.json | 61 ++++++++ .../resource-config.json | 19 +++ .../reflect-config.json | 25 ++++ .../reflect-config.json | 20 +++ .../resource-config.json | 7 + powertools-e2e-tests/pom.xml | 27 +--- .../amazon/lambda/powertools/BatchE2ET.java | 96 +++++++------ .../lambda/powertools/IdempotencyE2ET.java | 11 +- .../lambda/powertools/LargeMessageE2ET.java | 59 +++++--- .../LargeMessageIdempotentE2ET.java | 64 ++++++--- .../amazon/lambda/powertools/TracingE2ET.java | 36 ++--- .../lambda/powertools/ValidationALBE2ET.java | 129 +++++++++-------- .../powertools/ValidationApiGWE2ET.java | 135 +++++++++--------- .../testutils/DataNotReadyException.java | 25 ++++ .../powertools/testutils/Infrastructure.java | 24 ++-- .../powertools/testutils/RetryUtils.java | 75 ++++++++++ .../metrics/MetricDataNotFoundException.java | 27 ++++ .../testutils/metrics/MetricsFetcher.java | 34 ++--- .../testutils/tracing/TraceFetcher.java | 79 ++++------ .../tracing/TraceNotFoundException.java | 27 ++++ 26 files changed, 724 insertions(+), 342 deletions(-) create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/DataNotReadyException.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/RetryUtils.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricDataNotFoundException.java create mode 100644 powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceNotFoundException.java 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 16109778d..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 @@ -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/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index b1bc14c05..67bcee662 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -1,5 +1,5 @@ <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> @@ -21,6 +21,14 @@ <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> @@ -57,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/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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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/pom.xml b/powertools-e2e-tests/pom.xml index 1b36d4701..ccd87e95e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -40,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> @@ -66,72 +64,62 @@ <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.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.20.0</version> </dependency> - <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</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> @@ -226,9 +214,6 @@ <includes> <include>**/*E2ET.java</include> </includes> - <excludes> - <exclude>**/TracingE2ET.java</exclude> - </excludes> </configuration> </plugin> </plugins> @@ -257,10 +242,8 @@ <include>**/MetricsE2ET.java</include> <include>**/LoggingE2ET.java</include> <include>**/ParametersE2ET.java</include> + <include>**/TracingE2ET.java</include> </includes> - <excludes> - <exclude>**/TracingE2ET.java</exclude> - </excludes> <systemPropertyVariables> <graalvm.enabled>true</graalvm.enabled> </systemPropertyVariables> 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..292f46bfa 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,21 +21,23 @@ 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.Timeout; + import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -public class IdempotencyE2ET { +class IdempotencyE2ET { private static Infrastructure infrastructure; private static String functionName; @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) - public static void setup() { + static void setup() { String random = UUID.randomUUID().toString().substring(0, 6); infrastructure = Infrastructure.builder() .testName(IdempotencyE2ET.class.getSimpleName()) @@ -47,14 +49,14 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_ttlNotExpired_sameResult_ttlExpired_differentResult() throws InterruptedException { + void test_ttlNotExpired_sameResult_ttlExpired_differentResult() throws InterruptedException { // GIVEN String event = "{\"message\":\"TTL 10sec\"}"; @@ -65,6 +67,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 d9c3ef749..74247ca2e 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 @@ -36,9 +36,11 @@ 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 { +class LargeMessageE2ET { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageE2ET.class); private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); @@ -62,7 +64,7 @@ public class LargeMessageE2ET { @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; @@ -84,14 +86,14 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @AfterEach - public void reset() { + void reset() { if (messageId != null) { Map<String, AttributeValue> itemToDelete = new HashMap<>(); itemToDelete.put("functionName", AttributeValue.builder().s(functionName).build()); @@ -102,25 +104,24 @@ public void reset() { } @Test - public void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException, InterruptedException { - // given + void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException { + // 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"); + 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 + // WHEN client.sendMessage(SendMessageRequest .builder() .queueUrl(queueUrl) .messageBody(bigMessage) .build()); } - Thread.sleep(30000); // wait for function to be executed - // then + // THEN QueryRequest request = QueryRequest .builder() .tableName(tableName) @@ -128,8 +129,17 @@ 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); @@ -137,8 +147,8 @@ public void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException, In } @Test - public void smallSQSMessage_shouldNotReadFromS3() throws IOException, InterruptedException { - // given + void smallSQSMessage_shouldNotReadFromS3() { + // GIVEN final ExtendedClientConfiguration extendedClientConfig = new ExtendedClientConfiguration() .withPayloadSupportEnabled(s3Client, bucketName); try (AmazonSQSExtendedClient client = new AmazonSQSExtendedClient( @@ -146,16 +156,14 @@ public void smallSQSMessage_shouldNotReadFromS3() throws IOException, Interrupte extendedClientConfig)) { String message = "Hello World"; - // when + // WHEN client.sendMessage(SendMessageRequest .builder() .queueUrl(queueUrl) .messageBody(message) .build()); - Thread.sleep(30000); // wait for function to be executed - - // then + // THEN QueryRequest request = QueryRequest .builder() .tableName(tableName) @@ -163,8 +171,17 @@ public void smallSQSMessage_shouldNotReadFromS3() throws IOException, Interrupte .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("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( 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 ef342ea13..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 = 15000; - // 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/TracingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java index d2a5ceed1..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,6 +15,7 @@ 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; @@ -33,21 +34,21 @@ 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( - Map.of("POWERTOOLS_SERVICE_NAME", service, + Map.of("POWERTOOLS_SERVICE_NAME", SERVICE, "POWERTOOLS_TRACER_CAPTURE_RESPONSE", "true")) .build(); Map<String, String> outputs = infrastructure.deploy(); @@ -55,14 +56,14 @@ public static void setup() { } @AfterAll - public static void tearDown() { + static void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } @Test - public void test_tracing() { + void test_tracing() { // GIVEN final String message = "Hello World"; final String event = String.format("{\"message\":\"%s\"}", message); @@ -79,25 +80,18 @@ public void test_tracing() { .build() .fetchTrace(); - assertThat(trace.getSubsegments()).hasSize(2); - - // We need to filter segments based on name because they are not returned in-order from the X-Ray API - // The Init segment is created by default for Lambda functions in X-Ray - final SubSegment initSegment = trace.getSubsegments().stream() - .filter(subSegment -> subSegment.getName().equals("Init")) - .findFirst().orElse(null); - assertThat(initSegment.getName()).isEqualTo("Init"); - assertThat(initSegment.getAnnotations()).isNull(); + assertThat(trace.getSubsegments()).hasSize(1); final SubSegment handleRequestSegment = trace.getSubsegments().stream() - .filter(subSegment -> subSegment.getName().equals("## handleRequest")) + .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.getAnnotations()).containsEntry("Service", SERVICE); assertThat(handleRequestSegment.getMetadata()).hasSize(1); - final Map<String, Object> metadata = (Map<String, Object>) handleRequestSegment.getMetadata().get(service); + final Map<String, Object> metadata = (Map<String, Object>) handleRequestSegment.getMetadata().get(SERVICE); assertThat(metadata).containsEntry("handleRequest response", result); assertThat(handleRequestSegment.getSubsegments()).hasSize(2); @@ -108,14 +102,14 @@ public void test_tracing() { assertThat(sub.getName()).isIn("## internal_stuff", "## buildMessage"); SubSegment buildMessage = handleRequestSegment.getSubsegments().stream() - .filter(subSegment -> subSegment.getName().equals("## buildMessage")) + .filter(subSegment -> "## buildMessage".equals(subSegment.getName())) .findFirst().orElse(null); assertThat(buildMessage).isNotNull(); assertThat(buildMessage.getAnnotations()).hasSize(1); assertThat(buildMessage.getAnnotations()).containsEntry("message", message); assertThat(buildMessage.getMetadata()).hasSize(1); final Map<String, Object> buildMessageSegmentMetadata = (Map<String, Object>) buildMessage.getMetadata() - .get(service); + .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 index 41696943a..bf90851cb 100644 --- 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 @@ -38,67 +38,70 @@ class ValidationALBE2ET { - private static final ObjectMapper objectMapper = new ObjectMapper(); - - private static Infrastructure infrastructure; - private static String functionName; - - @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public 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 - public static void tearDown() { - if (infrastructure != null) { - infrastructure.destroy(); - } - } - - @Test - void test_validInboundSQSEvent() throws IOException { - InputStream inputStream = this.getClass().getResourceAsStream("/validation/valid_alb_in_out_event.json"); - String validEvent = IOUtils.toString(inputStream, 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 { - InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_alb_in_event.json"); - String invalidEvent = IOUtils.toString(inputStream, 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 { - InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_alb_out_event.json"); - String invalidEvent = IOUtils.toString(inputStream, 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"); - } + 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 index 425399c95..2d1bf4657 100644 --- 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 @@ -38,70 +38,73 @@ class ValidationApiGWE2ET { - private static final ObjectMapper objectMapper = new ObjectMapper(); - - private static Infrastructure infrastructure; - private static String functionName; - - @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - public 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 - public static void tearDown() { - if (infrastructure != null) { - infrastructure.destroy(); - } - } - - @Test - void test_validInboundApiGWEvent() throws IOException { - InputStream inputStream = this.getClass().getResourceAsStream("/validation/valid_api_gw_in_out_event.json"); - String validEvent = IOUtils.toString(inputStream, 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 { - InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_api_gw_in_event.json"); - String invalidEvent = IOUtils.toString(inputStream, 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 { - InputStream inputStream = this.getClass().getResourceAsStream("/validation/invalid_api_gw_out_event.json"); - String invalidEvent = IOUtils.toString(inputStream, 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"); - } + 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/Infrastructure.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java index c65871a0a..ea5ac3342 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 @@ -20,6 +20,7 @@ import java.io.IOException; import java.nio.file.Paths; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -97,7 +98,7 @@ * `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); @@ -120,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; @@ -223,7 +223,7 @@ private Stack createStackWithLambda() { ? dockerConfig.createGraalVMBundlingOptions(pathToFunction, runtime) : dockerConfig.createJVMBundlingOptions(pathToFunction, runtime); - functionName = stackName + "-function"; + String functionName = stackName + "-function"; CfnOutput.Builder.create(e2eStack, FUNCTION_NAME_OUTPUT) .value(functionName) .build(); @@ -469,10 +469,18 @@ private Map<String, Asset> findAssets() { 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); @@ -483,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; 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..054e9aa8e --- /dev/null +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/RetryUtils.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.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) { + RetryConfig config = RetryConfig.from(DEFAULT_RETRY_CONFIG) + .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); + } +} 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 186e72d13..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 @@ -73,14 +70,14 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String 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,24 +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("{}, attempts: {}", s.getLastExceptionThatCausedRetry().getMessage(), 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..fc2d061cc 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,21 @@ 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.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.ObjectMapper; + import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -43,6 +39,7 @@ 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; /** @@ -50,8 +47,8 @@ */ public class TraceFetcher { - private static final ObjectMapper MAPPER = - new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + private static final ObjectMapper MAPPER = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 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 +85,12 @@ 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() - .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", TraceNotFoundException.class).get(); } /** @@ -122,22 +104,19 @@ private Trace getTrace(List<String> traceIds) { .traceIds(traceIds) .build()); if (!tracesResponse.hasTraces()) { - throw new RuntimeException("No trace found"); + throw new TraceNotFoundException("No trace found"); } 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()) { + getNestedSubSegments(document.getSubsegments(), traceRes, + Collections.emptyList()); } + } catch (JsonProcessingException e) { LOG.error("Failed to parse segment document: " + e.getMessage()); throw new RuntimeException(e); @@ -149,8 +128,7 @@ private Trace getTrace(List<String> traceIds) { } 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); @@ -179,12 +157,12 @@ private List<String> getTraceIds() { .filterExpression(filterExpression) .build()); if (!traceSummaries.hasTraceSummaries()) { - throw new RuntimeException("No trace id found"); + throw new TraceNotFoundException("No trace id found"); } - 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("No trace id found"); } return traceIds; } @@ -193,7 +171,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,7 +183,8 @@ public TraceFetcher build() { if (end == null) { end = start.plus(1, ChronoUnit.MINUTES); } - LOG.debug("Looking for traces from {} to {} with filter {}", start, end, filterExpression); + LOG.debug("Looking for traces from {} to {} with filter {} and excluded segments {}", start, end, + filterExpression, excludedSegments); return new TraceFetcher(start, end, filterExpression, excludedSegments); } @@ -236,8 +215,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); + } +} From 894ec0584a667e318e6b16cfc23361655af2367b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:48:13 +0200 Subject: [PATCH 0763/1008] chore: bump aws-actions/configure-aws-credentials from 4.2.1 to 4.3.1 (#2011) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 4.2.1 to 4.3.1. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/b47578312673ae6fa5b5096b330d9fbac3d116df...7474bc4690e29a8392af63c5b98e7449536d5c3a) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-version: 4.3.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index f9cbfe1ea..6de4dc4f7 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -41,7 +41,7 @@ jobs: 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@b47578312673ae6fa5b5096b330d9fbac3d116df + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 020b89b2a..2aef7a3f8 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -64,7 +64,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 @@ -97,7 +97,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a56301591..15a3cb23e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -278,7 +278,7 @@ jobs: 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@b47578312673ae6fa5b5096b330d9fbac3d116df + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} From e68637c02cc55a140d23d35cced575b3404d3943 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:48:52 +0200 Subject: [PATCH 0764/1008] chore: bump org.apache.maven.plugins:maven-surefire-plugin (#2013) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.2 to 3.5.3. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.5.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- pom.xml | 4 ++-- powertools-common/pom.xml | 2 +- powertools-logging/pom.xml | 4 ++-- powertools-logging/powertools-logging-log4j/pom.xml | 4 ++-- powertools-logging/powertools-logging-logback/pom.xml | 4 ++-- powertools-parameters/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-appconfig/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-dynamodb/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-secrets/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-ssm/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index c80a9566d..98e3d60e4 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -126,7 +126,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 96f5002fe..d77cfa80b 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -135,7 +135,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <systemPropertyVariables> <sqlite4java.library.path>${project.build.directory}/native-libs</sqlite4java.library.path> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index fcdf1ffef..fdf7699d8 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -99,7 +99,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.2.5</version> + <version>3.5.3</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 129095534..e953e23a6 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -73,7 +73,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.2.5</version> + <version>3.5.3</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/pom.xml b/pom.xml index a1b714540..62213ab2f 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ <maven-gpg-plugin.version>3.2.1</maven-gpg-plugin.version> <junit.version>5.10.0</junit.version> <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> - <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> + <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> @@ -613,7 +613,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <argLine> @{argLine} diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index adc86547d..ace7cbe5a 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -103,7 +103,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index e6b9d6603..e926caf45 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -129,7 +129,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 @@ -214,7 +214,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.5</version> + <version>3.5.3</version> <configuration> <environmentVariables> <AWS_LAMBDA_LOG_FORMAT>JSON</AWS_LAMBDA_LOG_FORMAT> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 836ded5ed..02f34e7c2 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -108,7 +108,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 @@ -223,7 +223,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <POWERTOOLS_SERVICE_NAME>testLog4j</POWERTOOLS_SERVICE_NAME> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 1ad566809..f9b41a584 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -101,7 +101,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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</argLine> </configuration> @@ -216,7 +216,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <POWERTOOLS_SERVICE_NAME>testLogback</POWERTOOLS_SERVICE_NAME> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 1bc7efb52..c2ac064e7 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -80,7 +80,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.5</version> + <version>3.5.3</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> @@ -106,7 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 06567c284..2498dbc41 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -95,7 +95,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 @@ -177,7 +177,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 39b1bf083..b2934e648 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -96,7 +96,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 @@ -178,7 +178,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 6d156e13c..dfb9edebe 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -96,7 +96,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 @@ -178,7 +178,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 3b7916e3e..bed3cd3d7 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -84,7 +84,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.1.2</version> + <version>3.5.3</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> @@ -110,7 +110,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index b83793200..fe629572b 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -106,7 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 9905d0f82..e55d11601 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -106,7 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 2fa82e708..d1ada1ed1 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -126,7 +126,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.2.3</version> + <version>3.5.3</version> <configuration> <argLine>-Dmockito.mock.maker=subclass -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 From ee995f7f0af2f427c26b3eb9ceccc53384164682 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 7 Aug 2025 17:26:25 +0200 Subject: [PATCH 0765/1008] docs(examples): Add Bazel example for core utilities (#2022) * Add Bazel example. * Remove double _deploy suffix. * Use default .bazelrc setting and re-enable strict dependency checks. * Remove unused log method. --- .../sam-bazel/.bazelrc | 3 + .../sam-bazel/.gitignore | 20 ++++ .../sam-bazel/BUILD.bazel | 78 +++++++++++++ .../sam-bazel/MODULE.bazel | 27 +++++ .../sam-bazel/README.md | 63 ++++++++++ .../sam-bazel/events/event.json | 63 ++++++++++ .../src/main/java/helloworld/App.java | 108 ++++++++++++++++++ .../src/main/java/helloworld/AppStream.java | 58 ++++++++++ .../sam-bazel/src/main/resources/log4j2.xml | 16 +++ .../sam-bazel/template.yaml | 66 +++++++++++ 10 files changed, 502 insertions(+) create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/.bazelrc create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/.gitignore create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/BUILD.bazel create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/MODULE.bazel create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/README.md create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/events/event.json create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/src/main/resources/log4j2.xml create mode 100644 examples/powertools-examples-core-utilities/sam-bazel/template.yaml 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-utilities/sam-bazel/events/event.json b/examples/powertools-examples-core-utilities/sam-bazel/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/events/event.json @@ -0,0 +1,63 @@ +{ + "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" + } + } + \ No newline at end of file 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-utilities/sam-bazel/src/main/resources/log4j2.xml b/examples/powertools-examples-core-utilities/sam-bazel/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e1fd14cea --- /dev/null +++ b/examples/powertools-examples-core-utilities/sam-bazel/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> \ No newline at end of file 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 From 2b2e96f01a57718df32e2cde9ab0c8a6ac5208e2 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 7 Aug 2025 18:14:02 +0200 Subject: [PATCH 0766/1008] chore(ci): Improve reliability of retries in TracingE2ET (#2018) * Expand timewindow for X-RAY trace summary search during E2E tests. * Add retries for trace subsegments as well. * Add log.debug for subsegement population. * Add retries for custom subsegment population. * Add longer retries for TracingE2ET and capability to pass custom retry config to RetryUtils. * Add 1 minute padding around MetricsFetcher. * Revert 1 minute time padding for MetricsFetcher. * Add retry loop around datapoint fetching for first metrics datapoints. --- powertools-e2e-tests/pom.xml | 10 +++ .../amazon/lambda/powertools/MetricsE2ET.java | 14 ++-- .../powertools/testutils/RetryUtils.java | 32 ++++++++- .../testutils/tracing/TraceFetcher.java | 66 +++++++++++++++---- 4 files changed, 103 insertions(+), 19 deletions(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index ccd87e95e..d12395676 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -150,6 +150,16 @@ <version>2.4</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> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> 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 feb9537d5..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 @@ -34,7 +34,9 @@ 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; @@ -89,16 +91,20 @@ void test_recordMetrics() { { "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")); + 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, 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 index 054e9aa8e..ce64f04ea 100644 --- 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 @@ -47,7 +47,21 @@ private RetryUtils() { */ @SafeVarargs public static Retry createRetry(String name, Class<? extends Throwable>... retryOnThrowables) { - RetryConfig config = RetryConfig.from(DEFAULT_RETRY_CONFIG) + 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(); @@ -72,4 +86,20 @@ public static <T> Supplier<T> withRetry(Supplier<T> supplier, String name, 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/tracing/TraceFetcher.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java index fc2d061cc..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,6 +14,7 @@ package software.amazon.lambda.powertools.testutils.tracing; +import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -27,8 +28,11 @@ 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; @@ -46,9 +50,10 @@ * 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")); @@ -90,7 +95,12 @@ public Trace fetchTrace() { return getTrace(traceIds); }; - return RetryUtils.withRetry(supplier, "trace-fetcher", TraceNotFoundException.class).get(); + RetryConfig customConfig = RetryConfig.custom() + .maxAttempts(120) // 120 attempts over 10 minutes + .waitDuration(Duration.ofSeconds(5)) // 5 seconds between attempts + .build(); + + return RetryUtils.withRetry(supplier, "trace-fetcher", customConfig, TraceNotFoundException.class).get(); } /** @@ -104,8 +114,9 @@ private Trace getTrace(List<String> traceIds) { .traceIds(traceIds) .build()); if (!tracesResponse.hasTraces()) { - throw new TraceNotFoundException("No trace found"); + throw new TraceNotFoundException(String.format("No trace found for traceIds %s", traceIds)); } + Trace traceRes = new Trace(); tracesResponse.traces().forEach(trace -> { if (trace.hasSegments()) { @@ -113,17 +124,31 @@ private Trace getTrace(List<String> traceIds) { try { SegmentDocument document = MAPPER.readValue(segment.document(), SegmentDocument.class); if ("AWS::Lambda::Function".equals(document.getOrigin()) && document.hasSubsegments()) { - getNestedSubSegments(document.getSubsegments(), traceRes, - Collections.emptyList()); + 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; } @@ -149,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 TraceNotFoundException("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()); if (traceIds.isEmpty()) { - throw new TraceNotFoundException("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; } @@ -183,9 +217,13 @@ public TraceFetcher build() { if (end == null) { end = start.plus(1, ChronoUnit.MINUTES); } - LOG.debug("Looking for traces from {} to {} with filter {} and excluded segments {}", start, end, - filterExpression, excludedSegments); - 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) { From 68cad233ff932aff562b27b3368fead7c8becb53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:02:08 +0200 Subject: [PATCH 0767/1008] build(deps): bump aws.xray.recorder.version from 2.18.2 to 2.18.3 (#1986) * build(deps): bump aws.xray.recorder.version from 2.18.2 to 2.18.3 Bumps `aws.xray.recorder.version` from 2.18.2 to 2.18.3. Updates `com.amazonaws:aws-xray-recorder-sdk-core` from 2.18.2 to 2.18.3 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.18.2...v2.18.3) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core` from 2.18.2 to 2.18.3 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.18.2...v2.18.3) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2` from 2.18.2 to 2.18.3 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.18.2...v2.18.3) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.18.2 to 2.18.3 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.18.2...v2.18.3) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-version: 2.18.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-version: 2.18.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-version: 2.18.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-version: 2.18.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Add missing dependencies after removal of AWS SDK v1. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> Co-authored-by: Philipp Page <pagejep@amazon.com> --- pom.xml | 7 ++++++- powertools-tracing/pom.xml | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 62213ab2f..434427aaa 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> <aws.sdk.version>2.32.10</aws.sdk.version> - <aws.xray.recorder.version>2.18.2</aws.xray.recorder.version> + <aws.xray.recorder.version>2.19.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.3.0</lambda.core.version> @@ -225,6 +225,11 @@ <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>${jackson.version}</version> + </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d1ada1ed1..deaca4cf4 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -66,6 +66,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> From 56cc000d61a1d786540040c3b94bb4eeaa9687d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:09:22 +0200 Subject: [PATCH 0768/1008] chore: bump actions/download-artifact from 4.3.0 to 5.0.0 (#2017) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 5.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/d3f86a106a0bac45b974a628896c90dbdf5c8093...634f93cb2916e3fdff6788551b99b062d0335ce0) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15a3cb23e..eb8f422e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 with: name: source - name: Setup Java @@ -168,7 +168,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 with: name: source - name: Setup Java @@ -191,7 +191,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 with: name: source - name: Setup Java @@ -229,7 +229,7 @@ jobs: ref: ${{ env.RELEASE_COMMIT }} - id: download_source name: Download artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.6.1 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 with: name: source - id: setup-git From b1f06d74fee5890d41a6c57553c92c2fd51b5a90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:10:00 +0200 Subject: [PATCH 0769/1008] chore: bump io.github.ascopes:protobuf-maven-plugin from 3.6.1 to 3.7.0 (#2016) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.6.1 to 3.7.0. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.6.1...v3.7.0) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 7c87a964b..5fa7cd384 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.6.1</version> + <version>3.7.0</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 0e1a85b6d..9c8557fe1 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -181,7 +181,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.6.1</version> + <version>3.7.0</version> <executions> <execution> <id>generate-test-sources</id> From ca5f3abfe936712d7f62a8e12aeae75072f6a5ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:10:38 +0200 Subject: [PATCH 0770/1008] chore: bump aws.sdk.version from 2.32.10 to 2.32.16 (#2014) Bumps `aws.sdk.version` from 2.32.10 to 2.32.16. Updates `software.amazon.awssdk:bom` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:http-client-spi` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:url-connection-client` from 2.32.6 to 2.32.16 Updates `software.amazon.awssdk:dynamodb` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:s3` from 2.32.6 to 2.32.16 Updates `software.amazon.awssdk:lambda` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:kinesis` from 2.32.6 to 2.32.16 Updates `software.amazon.awssdk:cloudwatch` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:xray` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:sqs` from 2.32.6 to 2.32.16 Updates `software.amazon.awssdk:cloudformation` from 2.32.10 to 2.32.16 Updates `software.amazon.awssdk:sts` from 2.32.10 to 2.32.16 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.16 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.16 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.16 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 619ccfcba..b957fb8bc 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.10</aws.sdk.version> + <aws.sdk.version>2.32.18</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 434427aaa..7f6d6c658 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.10</aws.sdk.version> + <aws.sdk.version>2.32.18</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 81ca77a64628d285091aad7d6f850745004aa68a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:10:43 +0200 Subject: [PATCH 0771/1008] chore: bump org.codehaus.mojo:exec-maven-plugin from 3.3.0 to 3.5.1 (#2015) Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.3.0 to 3.5.1. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.3.0...3.5.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-version: 3.5.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 317277af9..576397dbb 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -25,7 +25,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.3.0</version> + <version>3.5.1</version> <configuration> <mainClass>cdk.CdkApp</mainClass> </configuration> From ccb7a6ec9acd1493200b69dc780520371ae9e052 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:32:35 +0200 Subject: [PATCH 0772/1008] chore: bump org.junit.jupiter:junit-jupiter from 5.11.1 to 5.13.4 (#2023) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework) from 5.11.1 to 5.13.4. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.11.1...r5.13.4) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-version: 5.13.4 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 576397dbb..00affa9f4 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -9,7 +9,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.208.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.11.1</junit.version> + <junit.version>5.13.4</junit.version> </properties> <build> <plugins> From ee79c204acc674fe26df4ce0cb062be5f38361f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:32:58 +0200 Subject: [PATCH 0773/1008] chore: bump aws.sdk.version from 2.32.6 to 2.32.18 (#2024) Bumps `aws.sdk.version` from 2.32.6 to 2.32.18. Updates `software.amazon.awssdk:url-connection-client` from 2.32.6 to 2.32.18 Updates `software.amazon.awssdk:sdk-core` from 2.32.6 to 2.32.18 Updates `software.amazon.awssdk:s3` from 2.32.6 to 2.32.18 Updates `software.amazon.awssdk:kinesis` from 2.32.6 to 2.32.18 Updates `software.amazon.awssdk:sqs` from 2.32.6 to 2.32.18 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.32.6 to 2.32.18 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.18 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.32.18 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.18 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.18 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.18 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.32.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 0d288f8ba..f4425d09e 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.32.6</sdk.version> + <sdk.version>2.32.18</sdk.version> </properties> <dependencies> From ca75f24cdadf3a06858bd3d0ebf8c74bd5fca905 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:33:26 +0200 Subject: [PATCH 0774/1008] chore: bump org.crac:crac from 1.4.0 to 1.5.0 (#2025) Bumps [org.crac:crac](https://github.com/crac/org.crac) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/crac/org.crac/releases) - [Commits](https://github.com/crac/org.crac/compare/1.4.0...1.5.0) --- updated-dependencies: - dependency-name: org.crac:crac dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7f6d6c658..94741fd81 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ <mockito.version>5.18.0</mockito.version> <mockito-junit-jupiter.version>5.18.0</mockito-junit-jupiter.version> <junit-pioneer.version>2.3.0</junit-pioneer.version> - <crac.version>1.4.0</crac.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. --> From bed5106ab6c4264239e44b8f561e657f9a9aa717 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:34:46 +0200 Subject: [PATCH 0775/1008] chore: bump github/codeql-action from 3.29.7 to 3.29.8 (#2027) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.7 to 3.29.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/51f77329afa6477de8c49fc9c7046c15b9a4e79d...76621b61decf072c1cee8dd1ce2d2a82d33c17ed) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 6367d2452..023ff41fd 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5 with: sarif_file: results.sarif From 58e09cbf812dd25e177e6c4cb5964dea763f8e8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 13:30:17 +0100 Subject: [PATCH 0776/1008] chore: bump com.amazonaws:aws-lambda-java-runtime-interface-client (#2026) Bumps [com.amazonaws:aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs) from 2.8.2 to 2.8.3. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-runtime-interface-client dependency-version: 2.8.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index d0fb3b8ea..55a23440f 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.2</version> + <version>2.8.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index fdf7699d8..aef7f1f97 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.2</version> + <version>2.8.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index a67433a08..145275915 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.2</version> + <version>2.8.3</version> </dependency> </dependencies> From 6c84fb7d0c49debe55beb6cb0620f1e0e8b608ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:57:06 +0200 Subject: [PATCH 0777/1008] chore: bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.4.0 to 4.9.3.2 (#2010) * chore: bump com.github.spotbugs:spotbugs-maven-plugin Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.8.4.0 to 4.9.3.2. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.4.0...spotbugs-maven-plugin-4.9.3.2) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.3.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Make raiseOnEmptyMetrics an AtomicBoolean. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> Co-authored-by: Philipp Page <pagejep@amazon.com> --- pom.xml | 2 +- .../powertools/metrics/internal/EmfMetricsLogger.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 94741fd81..e35e13d52 100644 --- a/pom.xml +++ b/pom.xml @@ -591,7 +591,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.8.4.0</version> + <version>4.9.3.2</version> <executions> <execution> <id>test</id> 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 index 1eedd270d..2f6f9e689 100644 --- 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 @@ -50,7 +50,7 @@ public class EmfMetricsLogger implements Metrics { private final software.amazon.cloudwatchlogs.emf.logger.MetricsLogger emfLogger; private final EnvironmentProvider environmentProvider; - private boolean raiseOnEmptyMetrics = false; + private AtomicBoolean raiseOnEmptyMetrics = new AtomicBoolean(false); private String namespace; private Map<String, String> defaultDimensions = new HashMap<>(); private final AtomicBoolean hasMetrics = new AtomicBoolean(false); @@ -133,7 +133,7 @@ public void setNamespace(String namespace) { @Override public void setRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics) { - this.raiseOnEmptyMetrics = raiseOnEmptyMetrics; + this.raiseOnEmptyMetrics.set(raiseOnEmptyMetrics); } @Override @@ -159,7 +159,7 @@ public void flush() { Validator.validateNamespace(namespace); if (!hasMetrics.get()) { - if (raiseOnEmptyMetrics) { + if (raiseOnEmptyMetrics.get()) { throw new IllegalStateException("No metrics were emitted"); } else { LOGGER.warn("No metrics were emitted"); From 174a19fd9ca256ec983f90bfabceddbfbba6330c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:47:22 +0200 Subject: [PATCH 0778/1008] chore: bump co.elastic.logging:logback-ecs-encoder from 1.6.0 to 1.7.0 (#2028) Bumps [co.elastic.logging:logback-ecs-encoder](https://github.com/elastic/ecs-logging-java) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/elastic/ecs-logging-java/releases) - [Commits](https://github.com/elastic/ecs-logging-java/compare/v1.6.0...v1.7.0) --- updated-dependencies: - dependency-name: co.elastic.logging:logback-ecs-encoder dependency-version: 1.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e35e13d52..49d6c111c 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ <jmespath.version>0.6.0</jmespath.version> <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.18.0</versions-maven-plugin.version> - <elastic.version>1.6.0</elastic.version> + <elastic.version>1.7.0</elastic.version> <mockito.version>5.18.0</mockito.version> <mockito-junit-jupiter.version>5.18.0</mockito-junit-jupiter.version> <junit-pioneer.version>2.3.0</junit-pioneer.version> From 877042bf7fc0053cc21facbb4924b52289c4af69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:47:39 +0200 Subject: [PATCH 0779/1008] chore: bump aws.sdk.version from 2.32.18 to 2.32.19 (#2029) Bumps `aws.sdk.version` from 2.32.18 to 2.32.19. Updates `software.amazon.awssdk:url-connection-client` from 2.32.18 to 2.32.19 Updates `software.amazon.awssdk:sdk-core` from 2.32.18 to 2.32.19 Updates `software.amazon.awssdk:s3` from 2.32.18 to 2.32.19 Updates `software.amazon.awssdk:kinesis` from 2.32.18 to 2.32.19 Updates `software.amazon.awssdk:sqs` from 2.32.18 to 2.32.19 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.32.18 to 2.32.19 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.19 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.32.19 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.19 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.19 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.19 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.32.19 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index f4425d09e..3b8f8b509 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.32.18</sdk.version> + <sdk.version>2.32.19</sdk.version> </properties> <dependencies> From ab66f95f65ad19375012de23c655aec20bfafd7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:47:55 +0200 Subject: [PATCH 0780/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.208.0 to 2.210.0 (#2030) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.208.0 to 2.210.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.208.0...v2.210.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.210.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 00affa9f4..45826e3c8 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.2.1</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.208.0</cdk.version> + <cdk.version>2.210.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.13.4</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index d12395676..73ac6c638 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.208.0</cdk.version> + <cdk.version>2.210.0</cdk.version> </properties> <dependencies> From 74d3e66481a9d0c0bfae1dc5278e81a5c9ab1788 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:48:10 +0200 Subject: [PATCH 0781/1008] chore: bump org.assertj:assertj-core from 3.27.3 to 3.27.4 (#2031) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.3 to 3.27.4. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.27.3...assertj-build-3.27.4) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49d6c111c..957703825 100644 --- a/pom.xml +++ b/pom.xml @@ -334,7 +334,7 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.27.3</version> + <version>3.27.4</version> <scope>test</scope> <exclusions> <exclusion> From f21054eae93252fd22e52c0c7402291e2768427a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:51:32 +0200 Subject: [PATCH 0782/1008] chore(ci): bump version to 2.3.0 (#2034) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 44 files changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index d5f240b6e..c7d3c6b71 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index bbf2f991e..f1cb326ce 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 3b8f8b509..3e9840e7e 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 98302aab9..b2951b28b 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.2.1718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.3.0718 ``` ### Deploy with CDK @@ -32,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-2.2.1718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.3.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index b957fb8bc..c74a9e9ad 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 67705c89e..4801dc591 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 45826e3c8..e2caf8b68 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.210.0</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 124c7b60b..8770d5b31 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.2.1' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.2.1' - aspect 'software.amazon.lambda:powertools-metrics:2.2.1' + aspect 'software.amazon.lambda:powertools-tracing:2.3.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.3.0' + aspect 'software.amazon.lambda:powertools-metrics:2.3.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 25ff24fac..ca0604517 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.2.1") - aspect("software.amazon.lambda:powertools-logging-log4j:2.2.1") - aspect("software.amazon.lambda:powertools-metrics:2.2.1") + aspect("software.amazon.lambda:powertools-tracing:2.3.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.3.0") + aspect("software.amazon.lambda:powertools-metrics:2.3.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 55a23440f..3e2477719 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 0208798fc..578897e86 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index a715553fb..2d795831c 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 98e3d60e4..a69f4a140 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index d77cfa80b..43e34a48d 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 5fa7cd384..ca208aff2 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index aef7f1f97..1641817aa 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index e953e23a6..44641102f 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 145275915..3f6dc295f 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 78bb6caad..3d9d4159e 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1227acc42..3ab151aec 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.2.1</version> + <version>2.3.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index e258d8c5a..45ea8e64b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,7 +121,7 @@ extra_javascript: extra: powertools: - version: 2.2.1 + version: 2.3.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 957703825..69686d508 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 74eae5726..bbdc5b0c2 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index b4d247178..355e8a3ed 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index ace7cbe5a..9f8456842 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 73ac6c638..fb88cd60f 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 08eefc473..e20d90565 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 0f2c2dec1..6f15bfc3d 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index d63719bbe..ed5b8d5c0 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 9c8557fe1..cd696d103 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index c9bfb2476..d61dacf48 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index e926caf45..7757be572 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 02f34e7c2..b28f307a6 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index f9b41a584..e44d97377 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 7e59bdbf9..57da447dd 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index c2ac064e7..d43221b5e 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 2498dbc41..4d650c58b 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index b2934e648..1834b229d 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index dfb9edebe..ef032f0c3 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index bed3cd3d7..fcb7b5311 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index fe629572b..ea29ffea2 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.2.1</version> + <version>2.3.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e55d11601..d6cc33891 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index deaca4cf4..cb7f8218f 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index b845bdb20..30e56802f 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.2.1</version> + <version>2.3.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 777e12853a8f83eb3a55349903c306eb2e25359e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:16:40 +0200 Subject: [PATCH 0783/1008] chore: bump dev.aspectj:aspectj-maven-plugin from 1.14 to 1.14.1 (#2037) Bumps [dev.aspectj:aspectj-maven-plugin](https://github.com/dev-aspectj/aspectj-maven-plugin) from 1.14 to 1.14.1. - [Release notes](https://github.com/dev-aspectj/aspectj-maven-plugin/releases) - [Commits](https://github.com/dev-aspectj/aspectj-maven-plugin/compare/aspectj-maven-plugin-1.14...aspectj-maven-plugin-1.14.1) --- updated-dependencies: - dependency-name: dev.aspectj:aspectj-maven-plugin dependency-version: 1.14.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 3e9840e7e..99f979cf6 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -86,7 +86,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c74a9e9ad..b845b89ed 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -87,7 +87,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 4801dc591..6e344ff87 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -66,7 +66,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 578897e86..dc53d834d 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -52,7 +52,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 2d795831c..28b68a930 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -53,7 +53,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index a69f4a140..925c1a69f 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -53,7 +53,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 43e34a48d..39f639aae 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -79,7 +79,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index ca208aff2..390f999a7 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -91,7 +91,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 1641817aa..24ccd3642 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -104,7 +104,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 44641102f..a67895bd5 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -78,7 +78,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 3d9d4159e..7598f9930 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -49,7 +49,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 3ab151aec..90d89546e 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -75,7 +75,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/pom.xml b/pom.xml index 69686d508..9ccf1065c 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ <maven-source-plugin.version>3.3.1</maven-source-plugin.version> <maven-gpg-plugin.version>3.2.1</maven-gpg-plugin.version> <junit.version>5.10.0</junit.version> - <aspectj-maven-plugin.version>1.14</aspectj-maven-plugin.version> + <aspectj-maven-plugin.version>1.14.1</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index b28f307a6..88e1ced39 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -193,7 +193,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index e44d97377..41750ec70 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -186,7 +186,7 @@ <plugin> <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.14</version> + <version>1.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From 7520ec2e8058f6814a995bad9bfd418fcef0ab98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:17:09 +0200 Subject: [PATCH 0784/1008] chore: bump github/codeql-action from 3.29.8 to 3.29.9 (#2038) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.8 to 3.29.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/76621b61decf072c1cee8dd1ce2d2a82d33c17ed...df559355d593797519d70b90fc8edd5db049e7a2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 023ff41fd..c482a0c3e 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5 + uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 with: sarif_file: results.sarif From b90ce6caf93d589e5f2845c3a712775fb1868d6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:18:28 +0200 Subject: [PATCH 0785/1008] chore: bump org.apache.maven.plugins:maven-deploy-plugin (#2040) Bumps [org.apache.maven.plugins:maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.1.2 to 3.1.4. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.1.2...maven-deploy-plugin-3.1.4) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-version: 3.1.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 4 ++-- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index f1cb326ce..2c561b00f 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -52,7 +52,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 99f979cf6..7c050e76b 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -147,7 +147,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index b845b89ed..4180db75d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -144,7 +144,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 6e344ff87..e02a636bf 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -132,7 +132,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index e2caf8b68..0d3a21e80 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -34,7 +34,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 3e2477719..302fa8074 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -143,7 +143,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index dc53d834d..3df44f441 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -117,7 +117,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 28b68a930..c4dc9c099 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -118,7 +118,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 925c1a69f..f02189948 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -118,7 +118,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 39f639aae..eb235437b 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -176,7 +176,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 24ccd3642..815506325 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -161,7 +161,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index a67895bd5..f10a20aab 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -108,7 +108,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 3f6dc295f..e660aeba2 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -46,7 +46,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 7598f9930..64ee8c465 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -41,7 +41,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 90d89546e..bdd14e5fb 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -106,7 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index fb88cd60f..b89125da6 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -184,7 +184,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index ea29ffea2..abd2a02d2 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -83,7 +83,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> @@ -182,7 +182,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> <configuration> <skip>true</skip> </configuration> From a0969a778d4552592f0147949f32c9e543340def Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:18:41 +0200 Subject: [PATCH 0786/1008] chore: bump aws.sdk.version from 2.32.18 to 2.32.21 (#2041) Bumps `aws.sdk.version` from 2.32.18 to 2.32.21. Updates `software.amazon.awssdk:bom` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:http-client-spi` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:url-connection-client` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:dynamodb` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:s3` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:lambda` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:kinesis` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:cloudwatch` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:xray` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:sqs` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:cloudformation` from 2.32.18 to 2.32.21 Updates `software.amazon.awssdk:sts` from 2.32.18 to 2.32.21 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.21 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.21 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.21 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 4180db75d..e4154c024 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.18</aws.sdk.version> + <aws.sdk.version>2.32.21</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 9ccf1065c..9b09ffc1f 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.18</aws.sdk.version> + <aws.sdk.version>2.32.21</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From c746acfcbd191195551212d9a01166e319f34fe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:18:51 +0200 Subject: [PATCH 0787/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.210.0 to 2.211.0 (#2042) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.210.0 to 2.211.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.210.0...v2.211.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.211.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 0d3a21e80..f8fdc5bf0 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.3.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.210.0</cdk.version> + <cdk.version>2.211.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.13.4</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index b89125da6..be1efef45 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.210.0</cdk.version> + <cdk.version>2.211.0</cdk.version> </properties> <dependencies> From deb53dec112db41f84b931b7154bd7cf66b48208 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 14 Aug 2025 10:59:45 +0200 Subject: [PATCH 0788/1008] chore(ci): Remove non-PR triggers for verify dependencies workflow. (#2044) --- .github/workflows/security-dependencies-check.yml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 108ba26fe..8f9e4600b 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -6,20 +6,12 @@ # # Triggers: # - pull_request -# - push -# - workflow_dispatch -# - cron: daily at 12:00PM on: - pull_request: - workflow_dispatch: - push: - branches: [ main ] - schedule: - - cron: '0 12 * * *' # Run daily at 12:00 UTC + pull_request: name: Verify Dependencies -run-name: Verify Dependencies – ${{ github.event_name }} +run-name: Verify Dependencies – ${{ github.event_name }} permissions: contents: read @@ -36,4 +28,4 @@ jobs: - name: Verify Contents uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 with: - config-file: './.github/dependency-review-config.yml' \ No newline at end of file + config-file: './.github/dependency-review-config.yml' From ab467c6bc2c8b332149fa0f00cb95eeb91043d80 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi <dreamorosi@gmail.com> Date: Thu, 14 Aug 2025 16:53:10 +0200 Subject: [PATCH 0789/1008] docs: update readme (#2045) * docs: update readme * Update README.md Co-authored-by: Philipp Page <github@philipp.page> --------- Co-authored-by: Philipp Page <github@philipp.page> --- README.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c7d3c6b71..62d34e9c5 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# Powertools for AWS Lambda (Java) V2 +# Powertools for AWS Lambda (Java) -**This is pre-release code for Powertools for AWS Lambda (Java) V2! Please check out the `main` branch for the stable release** - -![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) [![V2 Build Status](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/pr_build.yml/badge.svg?branch=v2)](https://github.com/aws-powertools/powertools-lambda-java/actions/workflows/pr_build.yml) **MAVEN DEPLOY NOT DONE** [![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-java/branch/v2/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-java/tree/v2) +![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/preview)** | **[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 @@ -153,7 +153,7 @@ More info [here](https://github.com/aws-powertools/powertools-lambda-java/pull/1 ## Examples -See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/v1.18.0/examples)** 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). @@ -161,7 +161,7 @@ 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: @@ -170,15 +170,10 @@ The following companies, among others, use Powertools: * [Europace AG](https://europace.de/) * [Vertex Pharmaceuticals](https://www.vrtx.com/) -## Credits - -* [MkDocs](https://www.mkdocs.org/) -* [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) - ## 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 @@ -186,4 +181,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. From 7e9ff02ea344ceeb0c9aa2b9736d28532085726a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 09:15:05 +0200 Subject: [PATCH 0790/1008] chore: bump aws.sdk.version from 2.32.21 to 2.32.22 (#2046) Bumps `aws.sdk.version` from 2.32.21 to 2.32.22. Updates `software.amazon.awssdk:bom` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:http-client-spi` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:url-connection-client` from 2.32.19 to 2.32.22 Updates `software.amazon.awssdk:dynamodb` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:s3` from 2.32.19 to 2.32.22 Updates `software.amazon.awssdk:lambda` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:kinesis` from 2.32.19 to 2.32.22 Updates `software.amazon.awssdk:cloudwatch` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:xray` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:sqs` from 2.32.19 to 2.32.22 Updates `software.amazon.awssdk:cloudformation` from 2.32.21 to 2.32.22 Updates `software.amazon.awssdk:sts` from 2.32.21 to 2.32.22 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.22 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.22 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.22 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index e4154c024..6e260a4d8 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.21</aws.sdk.version> + <aws.sdk.version>2.32.22</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 9b09ffc1f..d649f9f09 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.21</aws.sdk.version> + <aws.sdk.version>2.32.22</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 439b342715409a74977fb19f731bd895e47a190e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:15:32 +0200 Subject: [PATCH 0791/1008] chore: bump com.google.protobuf:protobuf-java from 4.31.1 to 4.32.0 (#2050) Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 4.31.1 to 4.32.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-version: 4.32.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 390f999a7..7f7038564 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -12,7 +12,7 @@ <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.31.1</protobuf.version> + <protobuf.version>4.32.0</protobuf.version> </properties> <dependencies> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index cd696d103..f8504a3ec 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -36,7 +36,7 @@ <properties> <kafka-clients.version>4.0.0</kafka-clients.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.31.1</protobuf.version> + <protobuf.version>4.32.0</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> From 6204bd103306dd55aa6cf239d7741768f5e7a842 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:15:37 +0200 Subject: [PATCH 0792/1008] chore: bump aws.sdk.version from 2.32.22 to 2.32.23 (#2048) Bumps `aws.sdk.version` from 2.32.22 to 2.32.23. Updates `software.amazon.awssdk:bom` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:http-client-spi` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:url-connection-client` from 2.32.19 to 2.32.23 Updates `software.amazon.awssdk:dynamodb` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:s3` from 2.32.19 to 2.32.23 Updates `software.amazon.awssdk:lambda` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:kinesis` from 2.32.19 to 2.32.23 Updates `software.amazon.awssdk:cloudwatch` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:xray` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:sqs` from 2.32.19 to 2.32.23 Updates `software.amazon.awssdk:cloudformation` from 2.32.22 to 2.32.23 Updates `software.amazon.awssdk:sts` from 2.32.22 to 2.32.23 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.23 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.23 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.23 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 6e260a4d8..39b2f9edf 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.22</aws.sdk.version> + <aws.sdk.version>2.32.23</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index d649f9f09..bb2854c33 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.22</aws.sdk.version> + <aws.sdk.version>2.32.23</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 006f8ff61244649f59a47f2d4fcb63bc6288b336 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 18 Aug 2025 11:56:58 +0200 Subject: [PATCH 0793/1008] docs: Rename wrong POWERTOOLS_DISABLE_METRICS to correct POWERTOOLS_METRICS_DISABLED environment variable. (#2043) --- docs/core/metrics.md | 4 ++-- docs/index.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 35d08b140..31079a76e 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -540,7 +540,7 @@ The following example shows how to configure a custom `Metrics` Singleton using ### Suppressing metrics output -If you would like to suppress metrics output during your unit tests, you can use the `POWERTOOLS_DISABLE_METRICS` environment variable. For example, using Maven you can set in your build plugins: +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> @@ -548,7 +548,7 @@ If you would like to suppress metrics output during your unit tests, you can use <artifactId>maven-surefire-plugin</artifactId> <configuration> <environmentVariables> - <POWERTOOLS_DISABLE_METRICS>true</POWERTOOLS_DISABLE_METRICS> + <POWERTOOLS_METRICS_DISABLED>true</POWERTOOLS_METRICS_DISABLED> </environmentVariables> </configuration> </plugin> diff --git a/docs/index.md b/docs/index.md index 3c6b9506b..17f3b397b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ Powertools for AWS Lambda (Java) is a suite of utilities for AWS Lambda Function ???+ 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, + 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 From 30ffa57ff9e2b2379076175d921b83587716b1e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:13:01 +0200 Subject: [PATCH 0794/1008] chore: bump actions/checkout from 4.2.2 to 5.0.0 (#2036) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/11bd71901bbe5b1630ceea73d27597364c9af683...08c6903cd8c0fde910a37f88322edcfb5dd907a8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-build.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- .github/workflows/security-branch-protections.yml | 2 +- .github/workflows/security-dependencies-check.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 6de4dc4f7..bf241cc1b 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -32,7 +32,7 @@ jobs: environment: Docs steps: - name: Checkout Repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 with: fetch-depth: 0 - name: Build diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 45fef40b9..257d83eee 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -94,7 +94,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 2aef7a3f8..d05e2d650 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -56,7 +56,7 @@ jobs: - 21 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: @@ -89,7 +89,7 @@ jobs: - 21 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index 7ef10c1de..906fdabd8 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -29,7 +29,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index 754672448..c4668be21 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -40,7 +40,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb8f422e9..c90b035da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - id: version name: version uses: ./.github/actions/version @@ -224,7 +224,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ env.RELEASE_COMMIT }} - id: download_source @@ -271,7 +271,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Build run: | mkdir -p dist diff --git a/.github/workflows/security-branch-protections.yml b/.github/workflows/security-branch-protections.yml index 05a082b0b..af6477802 100644 --- a/.github/workflows/security-branch-protections.yml +++ b/.github/workflows/security-branch-protections.yml @@ -46,7 +46,7 @@ jobs: - v1 steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Fetch branch protections id: fetch env: diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 8f9e4600b..6233a9ad9 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -24,7 +24,7 @@ jobs: pull-requests: write steps: - name: Checkout Repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 with: diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index c482a0c3e..9026fac5a 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -35,7 +35,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Run Analysis From 091569965853d4401bc00921d132302122e14f0e Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:47:09 +0200 Subject: [PATCH 0795/1008] fix(ci): Update branch protection output (#2053) --- .github/branch_protection_settings/main.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/branch_protection_settings/main.json b/.github/branch_protection_settings/main.json index 9fc6cc07d..8ca32bb45 100644 --- a/.github/branch_protection_settings/main.json +++ b/.github/branch_protection_settings/main.json @@ -19,7 +19,7 @@ "required_pull_request_reviews": { "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_pull_request_reviews", "dismiss_stale_reviews": true, - "require_code_owner_reviews": false, + "require_code_owner_reviews": true, "require_last_push_approval": true, "required_approving_review_count": 1, "dismissal_restrictions": { @@ -37,7 +37,7 @@ }, "enforce_admins": { "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/enforce_admins", - "enabled": false + "enabled": true }, "required_linear_history": { "enabled": true From c7d2437e2f1f37b1eb4c8ced66faf6e3c49b894b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:40:39 +0200 Subject: [PATCH 0796/1008] chore: bump aws.sdk.version from 2.32.19 to 2.32.26 (#2060) Bumps `aws.sdk.version` from 2.32.19 to 2.32.26. Updates `software.amazon.awssdk:url-connection-client` from 2.32.19 to 2.32.26 Updates `software.amazon.awssdk:sdk-core` from 2.32.19 to 2.32.26 Updates `software.amazon.awssdk:s3` from 2.32.19 to 2.32.26 Updates `software.amazon.awssdk:kinesis` from 2.32.19 to 2.32.26 Updates `software.amazon.awssdk:sqs` from 2.32.19 to 2.32.26 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.32.19 to 2.32.26 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.26 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.32.26 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.26 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.26 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.26 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.32.26 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 7c050e76b..c456ace5a 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.32.19</sdk.version> + <sdk.version>2.32.26</sdk.version> </properties> <dependencies> From 8b84bc3c18b6e59a8cd9b81ecdd01aa919d0bdf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:40:51 +0200 Subject: [PATCH 0797/1008] chore: bump aws.sdk.version from 2.32.23 to 2.32.25 (#2054) Bumps `aws.sdk.version` from 2.32.23 to 2.32.25. Updates `software.amazon.awssdk:bom` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:http-client-spi` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:url-connection-client` from 2.32.19 to 2.32.25 Updates `software.amazon.awssdk:dynamodb` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:s3` from 2.32.19 to 2.32.25 Updates `software.amazon.awssdk:lambda` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:kinesis` from 2.32.19 to 2.32.25 Updates `software.amazon.awssdk:cloudwatch` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:xray` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:sqs` from 2.32.19 to 2.32.25 Updates `software.amazon.awssdk:cloudformation` from 2.32.23 to 2.32.25 Updates `software.amazon.awssdk:sts` from 2.32.23 to 2.32.25 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.25 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.25 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.25 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 39b2f9edf..476d7c228 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.23</aws.sdk.version> + <aws.sdk.version>2.32.25</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index bb2854c33..ba7366d1a 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.23</aws.sdk.version> + <aws.sdk.version>2.32.25</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 6a0ae2f8f38360f4bc17f633f8677c57b702c741 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:41:18 +0200 Subject: [PATCH 0798/1008] chore: bump actions/dependency-review-action from 4.7.1 to 4.7.2 (#2055) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.7.1 to 4.7.2. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/da24556b548a50705dd671f47852072ea4c105d9...bc41886e18ea39df68b1b1245f4184881938e050) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.7.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 6233a9ad9..3e0ca168c 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -26,6 +26,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents - uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 + uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2 with: config-file: './.github/dependency-review-config.yml' From f5e955bfa7cdabbe5d734c04a5cd433a604a1049 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:41:38 +0200 Subject: [PATCH 0799/1008] chore: bump github/codeql-action from 3.29.9 to 3.29.10 (#2056) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.9 to 3.29.10. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/df559355d593797519d70b90fc8edd5db049e7a2...96f518a34f7a870018057716cc4d7a5c014bd61c) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 9026fac5a..18b523f14 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 + uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.5 with: sarif_file: results.sarif From 92c87153fe70e42198670f29c4c5f681c56befb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:41:59 +0200 Subject: [PATCH 0800/1008] chore: bump io.github.ascopes:protobuf-maven-plugin from 3.7.0 to 3.8.0 (#2057) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.7.0 to 3.8.0. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.7.0...v3.8.0) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 7f7038564..956ca7d8d 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.7.0</version> + <version>3.8.0</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index f8504a3ec..75167ff92 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -181,7 +181,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.7.0</version> + <version>3.8.0</version> <executions> <execution> <id>generate-test-sources</id> From 20e4798da1fc66bbe418aa2fe553e5f4c1fa1bfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:42:11 +0200 Subject: [PATCH 0801/1008] chore: bump org.apache.maven.plugins:maven-javadoc-plugin (#2059) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.11.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ba7366d1a..e6347b224 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ <maven-surefire-plugin.version>3.2.5</maven-surefire-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.11.2</maven-javadoc-plugin.version> + <maven-javadoc-plugin.version>3.11.3</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> <junit.version>5.10.0</junit.version> From 4bf7abde78206e60f99d883a149b5adf9f2a6637 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:47:44 +0200 Subject: [PATCH 0802/1008] chore: bump squidfunk/mkdocs-material in /docs (#2058) Bumps squidfunk/mkdocs-material from `bb7b015` to `405aeb6`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 405aeb6dbf1fcddd1082993eacf288a46648ea56b846323f001aee619015a23b dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 858c60705..b3c40662a 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:bb7b015690d9fb5ef0dbc98ca3520f153aa43129fb96aec5ca54c9154dc3b729 +FROM squidfunk/mkdocs-material@sha256:405aeb6dbf1fcddd1082993eacf288a46648ea56b846323f001aee619015a23b COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From da2b6584c85fb74cbc735a11b4cd5a309ee4b858 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 22 Aug 2025 09:43:49 +0200 Subject: [PATCH 0803/1008] chore(ci): Add powertools-e2e-tests/handlers as module to capture it in GitHub actions version upgrades. (#2063) --- .../demo/kafka/protobuf/ProtobufProduct.java | 6 +++--- .../protobuf/ProtobufProductOrBuilder.java | 2 +- .../protobuf/ProtobufProductOuterClass.java | 6 +++--- pom.xml | 1 + powertools-e2e-tests/handlers/batch/pom.xml | 4 ++-- .../handlers/idempotency/pom.xml | 4 ++-- .../handlers/largemessage/pom.xml | 4 ++-- .../handlers/largemessage_idempotent/pom.xml | 4 ++-- powertools-e2e-tests/handlers/logging/pom.xml | 4 ++-- powertools-e2e-tests/handlers/metrics/pom.xml | 4 ++-- .../handlers/parameters/pom.xml | 4 ++-- powertools-e2e-tests/handlers/pom.xml | 21 +++++++++---------- powertools-e2e-tests/handlers/tracing/pom.xml | 4 ++-- .../handlers/validation-alb-event/pom.xml | 4 ++-- .../handlers/validation-apigw-event/pom.xml | 4 ++-- powertools-e2e-tests/pom.xml | 2 +- 16 files changed, 39 insertions(+), 39 deletions(-) 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 index 250f37090..21961ad2b 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.32.0 package org.demo.kafka.protobuf; @@ -18,8 +18,8 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, - /* patch= */ 1, + /* minor= */ 32, + /* patch= */ 0, /* suffix= */ "", ProtobufProduct.class.getName()); } 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 index b2d185f6d..347ea854a 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.32.0 package org.demo.kafka.protobuf; 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 index adbfdd73a..046283d9a 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.32.0 package org.demo.kafka.protobuf; @@ -12,8 +12,8 @@ private ProtobufProductOuterClass() {} com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, - /* patch= */ 1, + /* minor= */ 32, + /* patch= */ 0, /* suffix= */ "", ProtobufProductOuterClass.class.getName()); } diff --git a/pom.xml b/pom.xml index e6347b224..3e633d818 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,7 @@ <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> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 3b7238b4e..630ff7cfa 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>2.0.0</version> + <version>2.3.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> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index dfa97225a..8b9b015bc 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.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> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index ce3fbbdd5..3aa876bfc 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>2.0.0</version> + <version>2.3.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> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index e9e87da2b..dea0f7263 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.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> diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index f5eacf5c5..d4d42d593 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.0</version> </parent> <artifactId>e2e-test-handler-logging</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) logging</name> + <name>E2E test handler – Logging</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 7bdc75591..809f7faa2 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) Metrics</name> + <name>E2E test handler – Metrics</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 46e6dc1e5..d1cd4bf74 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> <packaging>jar</packaging> - <name>A Lambda function using powertools parameters</name> + <name>E2E test handler – Parameters</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 1b3eaf3e0..393e3fecb 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,13 +4,12 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.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>2.0.0</lambda.powertools.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> @@ -55,47 +54,47 @@ <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging-log4j</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>${lambda.powertools.version}</version> + <version>${project.version}</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parameters-appconfig</artifactId> - <version>${lambda.powertools.version}</version> + <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>${lambda.powertools.version}</version> + <version>${project.version}</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 67bcee662..304941b0c 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.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> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index 36695b9a4..c595b7201 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) validation</name> + <name>E2E test handler – Validation ALB event</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 8bb927778..bdcc6813b 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,12 +5,12 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.0.0</version> + <version>2.3.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> <packaging>jar</packaging> - <name>A Lambda function using Powertools for AWS Lambda (Java) validation</name> + <name>E2E test handler – Validation API Gateway event</name> <dependencies> <dependency> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index be1efef45..8b39b510e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -25,7 +25,7 @@ <artifactId>powertools-e2e-tests</artifactId> <name>Powertools for AWS Lambda (Java) - End-to-end tests</name> - <description>Powertools for AWS Lambda (Java)End-To-End Tests</description> + <description>Powertools for AWS Lambda (Java) – End-To-End Tests</description> <properties> <maven.compiler.source>11</maven.compiler.source> From cc2df502c84830a2426ff69e1f4dddf68004890b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:28:52 +0200 Subject: [PATCH 0804/1008] chore: bump aws.sdk.version from 2.32.25 to 2.32.27 (#2064) Bumps `aws.sdk.version` from 2.32.25 to 2.32.27. Updates `software.amazon.awssdk:bom` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:http-client-spi` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:url-connection-client` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:dynamodb` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:s3` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:lambda` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:kinesis` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:cloudwatch` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:xray` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:sqs` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:cloudformation` from 2.32.25 to 2.32.27 Updates `software.amazon.awssdk:sts` from 2.32.25 to 2.32.27 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.27 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.27 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.27 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 476d7c228..c85e68352 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.25</aws.sdk.version> + <aws.sdk.version>2.32.27</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 3e633d818..4ae3fccec 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.25</aws.sdk.version> + <aws.sdk.version>2.32.27</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> From 78e161f23a54d4539b2a582afad101ab2c0d7c19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:29:06 +0200 Subject: [PATCH 0805/1008] chore: bump org.apache.maven.plugins:maven-javadoc-plugin (#2065) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.11.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ae3fccec..c4d36cc31 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> <nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version> - <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version> + <maven-javadoc-plugin.version>3.11.3</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.13.4</junit.version> From d2aa836f1d0c6f90075698176ff2714bbb2e09a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:31:59 +0200 Subject: [PATCH 0806/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.211.0 to 2.212.0 (#2066) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.211.0 to 2.212.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.211.0...v2.212.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.212.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index f8fdc5bf0..bac4f78ff 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.3.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.211.0</cdk.version> + <cdk.version>2.212.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.13.4</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 8b39b510e..15ac7650e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.211.0</cdk.version> + <cdk.version>2.212.0</cdk.version> </properties> <dependencies> From 172f8945c98086aa8f3f92082a0d2613f3a99ed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:32:31 +0200 Subject: [PATCH 0807/1008] chore: bump actions/setup-java from 4.7.1 to 5.0.0 (#2067) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.7.1 to 5.0.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/c5195efecf7bdfc987ee8bae7a71cb8b11521c00...dded0888837ed1f317902acf8a20df0ad188d165) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 257d83eee..bde305dce 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -96,7 +96,7 @@ jobs: name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: distribution: corretto java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index d05e2d650..462bfbc34 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -58,7 +58,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} @@ -91,7 +91,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index 906fdabd8..41983f89b 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: java-version: 21 distribution: corretto diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index c4668be21..214125ed4 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: distribution: 'corretto' java-version: 21 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c90b035da..4e321cc49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,7 +137,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: distribution: corretto java-version: 21 @@ -172,7 +172,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: distribution: corretto java-version: ${{ matrix.java }} @@ -195,7 +195,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: distribution: corretto java-version: 21 From bbb591b116a6d21adaf74d1a80a825e9d4d2aac1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:23:14 +0200 Subject: [PATCH 0808/1008] chore: bump aws.sdk.version from 2.32.2 to 2.32.28 (#2068) Bumps `aws.sdk.version` from 2.32.2 to 2.32.28. Updates `software.amazon.awssdk:bom` from 2.32.2 to 2.32.28 Updates `software.amazon.awssdk:http-client-spi` from 2.32.27 to 2.32.28 Updates `software.amazon.awssdk:url-connection-client` from 2.32.26 to 2.32.28 Updates `software.amazon.awssdk:dynamodb` from 2.32.27 to 2.32.28 Updates `software.amazon.awssdk:s3` from 2.32.26 to 2.32.28 Updates `software.amazon.awssdk:lambda` from 2.32.27 to 2.32.28 Updates `software.amazon.awssdk:kinesis` from 2.32.26 to 2.32.28 Updates `software.amazon.awssdk:cloudwatch` from 2.32.27 to 2.32.28 Updates `software.amazon.awssdk:xray` from 2.32.27 to 2.32.28 Updates `software.amazon.awssdk:sqs` from 2.32.26 to 2.32.28 Updates `software.amazon.awssdk:cloudformation` from 2.32.27 to 2.32.28 Updates `software.amazon.awssdk:sts` from 2.32.27 to 2.32.28 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.28 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.28 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.28 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c85e68352..cfb757440 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.27</aws.sdk.version> + <aws.sdk.version>2.32.28</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index c4d36cc31..5896ffeb7 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.27</aws.sdk.version> + <aws.sdk.version>2.32.28</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 393e3fecb..a3ca9bef1 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -20,7 +20,7 @@ <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.32.2</aws.sdk.version> + <aws.sdk.version>2.32.28</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> From 8c397bc97f4d36e80208c677a287fd5c5b7a3504 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:23:32 +0200 Subject: [PATCH 0809/1008] chore: bump com.amazonaws:aws-lambda-java-runtime-interface-client (#2069) Bumps [com.amazonaws:aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs) from 2.8.2 to 2.8.3. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-runtime-interface-client dependency-version: 2.8.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index a3ca9bef1..68cf5c748 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -114,7 +114,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.2</version> + <version>2.8.3</version> </dependency> </dependencies> </dependencyManagement> From 2caf5dc12439c728aa271d0430707ceb83fc4c37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:23:53 +0200 Subject: [PATCH 0810/1008] chore: bump org.graalvm.buildtools:native-maven-plugin (#2070) Bumps [org.graalvm.buildtools:native-maven-plugin](https://github.com/graalvm/native-build-tools) from 0.10.6 to 0.11.0. - [Release notes](https://github.com/graalvm/native-build-tools/releases) - [Commits](https://github.com/graalvm/native-build-tools/compare/0.10.6...0.11.0) --- updated-dependencies: - dependency-name: org.graalvm.buildtools:native-maven-plugin dependency-version: 0.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 68cf5c748..17ac6825d 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -193,7 +193,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.10.6</version> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> From ab1540da9791ee8f1d1c27047a6366da47a429ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:24:58 +0200 Subject: [PATCH 0811/1008] chore: bump org.apache.maven.plugins:maven-shade-plugin (#2071) Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.0...maven-shade-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-version: 3.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 17ac6825d..8200b6ca3 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -17,7 +17,7 @@ <lambda.java.core>1.3.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.5.0</maven.shade.version> + <maven.shade.version>3.6.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.32.28</aws.sdk.version> From 55a4628a7dd4620ed483c623ad08f1ab823fd03c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:26:15 +0200 Subject: [PATCH 0812/1008] chore: bump log4j.version from 2.25.1 to 2.25.1 (#2072) Bumps `log4j.version` from 2.25.1 to 2.25.1. Updates `org.apache.logging.log4j:log4j-core` from 2.25.1 to 2.25.1 Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.25.1 to 2.25.1 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.25.1 to 2.25.1 Updates `org.apache.logging.log4j:log4j-api` from 2.25.1 to 2.25.1 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.20.0 to 2.25.1 Updates `org.apache.logging.log4j:log4j-jcl` from 2.25.1 to 2.25.1 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/handlers/logging/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index d4d42d593..f8b5689c8 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -20,7 +20,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-layout-template-json</artifactId> - <version>2.20.0</version> + <version>2.25.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> From 95b9c644323cffb3a4efbbfa6fd1e30b0abf6073 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:27:44 +0200 Subject: [PATCH 0813/1008] chore: bump github/codeql-action from 3.29.10 to 3.29.11 (#2073) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.10 to 3.29.11. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/96f518a34f7a870018057716cc4d7a5c014bd61c...3c3833e0f8c1c83d449a7478aa59c036a9165498) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 18b523f14..4047f0da2 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.5 + uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5 with: sarif_file: results.sarif From 90a2c1aa4df22fb11c4886930a9a701185f28f34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:27:59 +0200 Subject: [PATCH 0814/1008] chore: bump squidfunk/mkdocs-material in /docs (#2074) Bumps squidfunk/mkdocs-material from `405aeb6` to `1a4e939`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 1a4e939cfd62b90943b6a829eb8544933e4e94eab18b8ca1d0f912fa9a532c37 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index b3c40662a..4d3135506 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:405aeb6dbf1fcddd1082993eacf288a46648ea56b846323f001aee619015a23b +FROM squidfunk/mkdocs-material@sha256:1a4e939cfd62b90943b6a829eb8544933e4e94eab18b8ca1d0f912fa9a532c37 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 7bb7b29ee5133f4df93633233afa63a086642c6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:28:16 +0200 Subject: [PATCH 0815/1008] chore: bump sam/build-java21 (#2075) Bumps sam/build-java21 from `44e9166` to `6ad1664`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 6ad16645a48cbc9aa6ab9b707350d1976804f410bb47dd0f87bf106cd69ff36a dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 927a221c4..a4cacd9cb 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:44e9166767b12df862da6f76b4622f89e3b97a31bab73bfbb3b1df13515bfd23 +FROM public.ecr.aws/sam/build-java21@sha256:6ad16645a48cbc9aa6ab9b707350d1976804f410bb47dd0f87bf106cd69ff36a # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From cbdf81fb4be6f8b793a2fc4597ea91a1fa0c2a05 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 22 Aug 2025 15:32:42 +0200 Subject: [PATCH 0816/1008] chore(ci): Fix bug where docs were released with old version during release workflow. (#2076) --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e321cc49..08dfa90dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -272,6 +272,9 @@ jobs: - id: checkout name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + # Checkout PR branch to make sure we build the version-bumped docs + ref: ci-${{ github.run_id }} - name: Build run: | mkdir -p dist From a9ce7be3c56923c01e0c0721e144c1e3bc793d25 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 25 Aug 2025 13:59:00 +0200 Subject: [PATCH 0817/1008] chore(ci): Run unit tests for GraalVM as well during build. (#2047) * chore(ci): Run unit tests for GraalVM as well during build. * Test GraalVM unit tests on Mockito 5.16.0. * Fix native tests in powertools-serialization. Remove mockito dependency there. * Remove mockito from powertools-common. * Remove mockito from powertools-metrics and re-use TestLamdaContext from powertools-common. * Remove mockito from powertools-serialization. * Remove mockito from powertools-logging. * Remove mockito from powertools-logging-log4j. * Add --initialize-at-build-time=org.junit.platform.launcher.core.DiscoveryIssueNotifier * Remove mockito from powertools-logging-logback. * Remove mockito from powertools-tracing. * Build project first to make sure test-jar dependencies are available for graalvm unit tests. * Enable maven quite mode and search recursively for graalvm profiles to make sure sub-sub-modules are also covered. * Add back Mockito to powertools-common. * Simplify graalvm config for logback and log4j modules. * Simplify graalvm config in powertools-logging. * Cleanup GRM files for logback module. * Simplify GraalVM config for powertools-metrics, powertools-serialization, powertools-tracing. * Remove <! or newer version --> comment. * Simplify powertools-parameters-tests GraalVM config and fix issues in unit tests. * Simplify powertools-parameters-ssm GraalVM config and fix issues in unit tests. * Simplify powertools-parameters-secrets GraalVM config and fix issues in unit tests. * Simplify powertools-parameters-dynamodb GraalVM config and fix issues in unit tests. * Simplify powertools-parameters-appconfig GraalVM config and fix issues in unit tests. * Fix SonarCube finding. * Allowlist UPL-1.0 license. * Add TT ID to UPL license. * Enable Maven Central snapshot repo temporarily. * Potential fix for code scanning alert no. 54: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Disable verbose resource registration logs. --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/dependency-review-config.yml | 4 +- .github/workflows/check-build.yml | 65 ++++---- pom.xml | 22 ++- powertools-common/pom.xml | 58 +++---- .../internal/LambdaHandlerProcessorTest.java | 49 +++--- .../common/stubs/TestLambdaContext.java | 22 +-- powertools-logging/pom.xml | 64 ++------ .../powertools-logging-log4j/pom.xml | 68 +++----- .../PowerToolsResolverFactoryTest.java | 27 ++-- .../PowertoolsResolverArgumentsTest.java | 31 ++-- .../powertools-logging-logback/pom.xml | 70 +++------ .../jni-config.json | 4 - .../reflect-config.json | 148 ++++-------------- .../resource-config.json | 10 -- .../internal/LambdaEcsEncoderTest.java | 36 ++--- .../internal/LambdaJsonEncoderTest.java | 80 +++++----- .../internal/LambdaLoggingAspectTest.java | 66 ++++---- powertools-metrics/pom.xml | 73 ++------- .../metrics/ConfigurationPrecedenceTest.java | 10 +- .../internal/EmfMetricsLoggerTest.java | 8 +- .../internal/LambdaMetricsAspectTest.java | 18 +-- powertools-parameters/pom.xml | 53 +------ .../powertools-parameters-appconfig/pom.xml | 49 +++--- .../appconfig/AppConfigParamAspectTest.java | 1 + .../appconfig/AppConfigProviderTest.java | 6 +- .../powertools-parameters-dynamodb/pom.xml | 49 +++--- .../dynamodb/DynamoDbProviderTest.java | 25 +-- .../powertools-parameters-secrets/pom.xml | 49 +++--- .../secrets/SecretsProviderTest.java | 16 +- .../powertools-parameters-ssm/pom.xml | 51 +++--- .../powertools-parameters-tests/pom.xml | 49 +++--- .../parameters/BaseProviderTest.java | 10 +- .../ParamProvidersIntegrationTest.java | 17 +- powertools-serialization/pom.xml | 63 ++------ powertools-tracing/pom.xml | 59 ++----- .../powertools/tracing/TracingUtilsTest.java | 63 ++------ .../internal/LambdaTracingAspectTest.java | 62 +++----- 37 files changed, 544 insertions(+), 1011 deletions(-) rename powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.java => powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java (73%) diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml index 6d737ee55..2ea218503 100644 --- a/.github/dependency-review-config.yml +++ b/.github/dependency-review-config.yml @@ -27,4 +27,6 @@ allow-licenses: - 'BSD-3-Clause-No-Nuclear-License-2014' - 'BSD-3-Clause-No-Nuclear-Warranty' - 'BSD-3-Clause-Open-MPI' -comment-summary-in-pr: on-failure \ No newline at end of file + # TT: D290816995 + - 'UPL-1.0' +comment-summary-in-pr: on-failure diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index bde305dce..98e1eae6e 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -2,15 +2,13 @@ # # 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: -# The matrix build for this workflow is unusual, we need to make it dyanmic since -# we need to change java versions we build for depending on the branch. - +# Builds against Java 11, 17, and 21 which are the supported versions. on: workflow_dispatch: @@ -38,7 +36,7 @@ on: push: branches: - main - paths: # add other modules when there are under e2e tests + paths: - 'powertools-batch/**' - 'powertools-core/**' - 'powertools-cloudformation/**' @@ -60,34 +58,16 @@ on: - '.github/workflows/**' name: Build +permissions: + contents: read run-name: Build - ${{ github.event_name }} jobs: - setup: - runs-on: ubuntu-latest - outputs: - build_matrix: ${{ format('{0}{1}', steps.build_matrix_v1.outputs.build_matrix, steps.build_matrix_v1.outputs.build_matrix) }} - steps: - - id: base - name: Base - run: | - echo build_version=$(test ${{ github.ref }} == "v2" && 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" - build: + java-build: runs-on: ubuntu-latest strategy: matrix: java: - - 8 - 11 - 17 - 21 @@ -103,6 +83,35 @@ jobs: cache: maven - id: build-maven name: Build (Maven) - if: ${{ matrix.java != '8' }} run: | - mvn -B install --file pom.xml \ No newline at end of file + mvn -B -q install --file pom.xml + + graalvm-build: + runs-on: ubuntu-latest + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup GraalVM + uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1.3.5 + with: + java-version: "21" + distribution: "graalvm" + cache: maven + - id: graalvm-native-test + name: GraalVM Native Test + run: | + # Build the entire project first to ensure test-jar dependencies are available + mvn -B -q install -DskipTests + + # Find modules with graalvm-native profile and run tests recursively. + # This will make sure to discover new GraalVM supported modules automatically in the future. + find . -name "pom.xml" -path "./powertools-*" | while read module; do + if grep -q "<id>graalvm-native</id>" "$module"; then + module_dir=$(dirname "$module") + 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 + fi + done diff --git a/pom.xml b/pom.xml index 5896ffeb7..f6ccb5056 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,6 @@ <maven-javadoc-plugin.version>3.11.3</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> - <junit.version>5.10.0</junit.version> <aspectj-maven-plugin.version>1.14.1</aspectj-maven-plugin.version> <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version> <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> @@ -115,8 +114,8 @@ <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.18.0</versions-maven-plugin.version> <elastic.version>1.7.0</elastic.version> - <mockito.version>5.18.0</mockito.version> - <mockito-junit-jupiter.version>5.18.0</mockito-junit-jupiter.version> + <mockito.version>5.19.1-SNAPSHOT</mockito.version> + <mockito-junit-jupiter.version>5.19.1-SNAPSHOT</mockito-junit-jupiter.version> <junit-pioneer.version>2.3.0</junit-pioneer.version> <crac.version>1.5.0</crac.version> @@ -132,6 +131,21 @@ </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> @@ -323,7 +337,7 @@ <dependency> <groupId>org.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> - <version>2.0.0</version> + <version>${junit-pioneer.version}</version> <scope>test</scope> </dependency> <dependency> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 9f8456842..ea9baa98c 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/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-common</artifactId> @@ -77,13 +77,13 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-simple</artifactId> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> </dependencies> @@ -94,7 +94,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -105,7 +104,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -120,7 +121,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -129,7 +129,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -146,33 +146,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> @@ -192,5 +169,18 @@ <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/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index 15d7bccdb..17732cdf0 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -33,14 +33,14 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -class LambdaHandlerProcessorTest { +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; - private Signature signature = mock(Signature.class); - private ProceedingJoinPoint pjpMock = mock(ProceedingJoinPoint.class); +class LambdaHandlerProcessorTest { @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(); @@ -48,7 +48,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(); @@ -65,7 +65,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(); @@ -73,7 +73,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(); @@ -81,7 +81,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); @@ -91,7 +91,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); @@ -101,7 +101,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); @@ -111,7 +111,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); @@ -121,7 +121,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); @@ -131,7 +131,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); @@ -144,9 +144,9 @@ void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidContextArg() { void getXrayTraceId_present() { String traceID = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""; - Optional xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); + Optional<String> xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); - assertThat(xRayTraceId.isPresent()).isTrue(); + assertThat(xRayTraceId).isPresent(); assertThat(traceID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")).isEqualTo(xRayTraceId.get()); } @@ -161,7 +161,7 @@ void getXrayTraceId_notPresent() { @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); @@ -171,7 +171,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); @@ -181,7 +181,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); @@ -230,10 +230,15 @@ void serviceName_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; } } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java similarity index 73% rename from powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.java rename to powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java index c4f5e4455..6b66b66b7 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/testutils/TestContext.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java @@ -12,14 +12,14 @@ * */ -package software.amazon.lambda.powertools.metrics.testutils; +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; -/** - * Simple Lambda context implementation for unit tests - */ -public class TestContext implements Context { +public class TestLambdaContext implements Context { @Override public String getAwsRequestId() { return "test-request-id"; @@ -42,27 +42,27 @@ public String getFunctionName() { @Override public String getFunctionVersion() { - return "test-version"; + return "1"; } @Override public String getInvokedFunctionArn() { - return "test-arn"; + return "arn:aws:lambda:us-east-1:123456789012:function:test"; } @Override - public com.amazonaws.services.lambda.runtime.CognitoIdentity getIdentity() { + public CognitoIdentity getIdentity() { return null; } @Override - public com.amazonaws.services.lambda.runtime.ClientContext getClientContext() { + public ClientContext getClientContext() { return null; } @Override public int getRemainingTimeInMillis() { - return 1000; + return 30000; } @Override @@ -71,7 +71,7 @@ public int getMemoryLimitInMB() { } @Override - public com.amazonaws.services.lambda.runtime.LambdaLogger getLogger() { + public LambdaLogger getLogger() { return null; } } diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 7757be572..7a288870a 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/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> <parent> @@ -67,11 +67,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.slf4j</groupId> <artifactId>slf4j-simple</artifactId> @@ -112,18 +107,17 @@ <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> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -131,7 +125,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -142,20 +138,12 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -172,30 +160,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 88e1ced39..836f8c53f 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -51,11 +51,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.junit-pioneer</groupId> <artifactId>junit-pioneer</artifactId> @@ -91,18 +86,17 @@ <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> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -110,7 +104,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -121,20 +117,12 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -148,33 +136,16 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> @@ -234,5 +205,4 @@ </plugin> </plugins> </build> - </project> 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 index 4cf798a47..7ea81d690 100644 --- 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 @@ -17,37 +17,36 @@ 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 static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import com.amazonaws.services.lambda.runtime.Context; 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.Order; import org.junit.jupiter.api.Test; -import org.mockito.Mock; 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.handler.PowertoolsLogEnabled; @Order(1) class PowerToolsResolverFactoryTest { - @Mock private Context context; @BeforeEach void setUp() throws IllegalAccessException, IOException { - openMocks(this); MDC.clear(); writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); + 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(); @@ -58,7 +57,7 @@ void setUp() throws IllegalAccessException, IOException { } @AfterEach - void cleanUp() throws IOException{ + void cleanUp() throws IOException { FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); } @@ -70,7 +69,7 @@ void shouldLogInJsonFormat() { 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:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"function_version\":\"1\",\"service\":\"testLog4j\",\"timestamp\":") + "{\"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"); } @@ -81,15 +80,7 @@ void shouldLogInEcsFormat() { 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\":\"012345678910\",\"faas.id\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"faas.name\":\"testFunction\",\"faas.version\":\"1\",\"faas.memory\":1024,\"faas.execution\":\"RequestId\",\"faas.coldstart\":true,\"trace.id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\"}\n"); + "\"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"); } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn( - "arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(1024); - when(context.getAwsRequestId()).thenReturn("RequestId"); - } } 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 index 463ad043d..5432e45ae 100644 --- 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 @@ -16,11 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.contentOf; -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 java.io.File; import java.io.IOException; import java.nio.channels.FileChannel; @@ -29,27 +25,29 @@ 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.mockito.Mock; 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 { - @Mock private Context context; @BeforeEach void setUp() throws IOException { - openMocks(this); MDC.clear(); - setupContext(); + context = new TestLambdaContext(); try { FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); @@ -61,7 +59,7 @@ void setUp() throws IOException { @AfterEach void cleanUp() throws IOException { - //Make sure file is cleaned up before running full stack logging regression + // 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(); } @@ -74,7 +72,7 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { msg.setMessageId("1212abcd"); msg.setBody("plop"); msg.setEventSource("eb"); - msg.setAwsRegion("eu-west-1"); + msg.setAwsRegion("us-east-1"); SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); @@ -86,7 +84,7 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) .contains( - "\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + "\"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\\\"\""); // Reserved keys should be ignored @@ -106,7 +104,7 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { msg.setMessageId("1212abcd"); msg.setBody("plop"); msg.setEventSource("eb"); - msg.setAwsRegion("eu-west-1"); + msg.setAwsRegion("us-east-1"); SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); @@ -118,7 +116,7 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) .contains( - "\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + "\"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\\\"\""); @@ -131,11 +129,4 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { }); } - 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"); - } } diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 41750ec70..433a3774a 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -49,8 +49,10 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> <scope>test</scope> </dependency> <dependency> @@ -88,14 +90,6 @@ <profiles> <profile> <id>generate-graalvm-files</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -103,7 +97,12 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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</argLine> + <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> @@ -111,20 +110,12 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -136,37 +127,18 @@ </execution> </executions> <configuration> - <agent> - <enabled>true</enabled> - <defaultMode>Standard</defaultMode> - </agent> <imageName>powertools-logging-logback</imageName> <buildArgs> - <buildArg>--no-fallback</buildArg> - <buildArg>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable + --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> @@ -227,6 +199,4 @@ </plugin> </plugins> </build> - - </project> 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 index 2c4de0562..753dafdea 100644 --- 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 @@ -15,10 +15,6 @@ "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-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 index 0d3bdbe7e..683933a77 100644 --- 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 @@ -21,11 +21,7 @@ "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { - "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.services.lambda.runtime.Context" }, { "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$MessageAttribute", @@ -49,12 +45,6 @@ "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "methods":[{"name":"<init>","parameterTypes":[] }] }, -{ - "name":"com.sun.tools.attach.VirtualMachine" -}, -{ - "name":"java.io.FilePermission" -}, { "name":"java.io.IOException" }, @@ -66,132 +56,64 @@ }, { "name":"java.io.Serializable", - "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":[] }] + "queryAllDeclaredMethods":true }, { "name":"java.lang.Cloneable", "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, - "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.Iterable", + "queryAllDeclaredMethods":true }, { - "name":"java.lang.RuntimePermission" + "name":"java.lang.Object" }, { - "name":"java.lang.StackWalker" + "name":"java.lang.String" }, { - "name":"java.lang.String" + "name":"java.util.AbstractCollection", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.System", - "methods":[{"name":"getSecurityManager","parameterTypes":[] }] + "name":"java.util.AbstractList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.annotation.Retention", - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true + "name":"java.util.AbstractMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.annotation.Target", + "name":"java.util.Arrays$ArrayList", + "allDeclaredFields":true, "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.net.NetPermission" -}, -{ - "name":"java.net.SocketPermission" -}, -{ - "name":"java.net.URLPermission", - "methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }] -}, -{ - "name":"java.security.AccessController", - "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] + "name":"java.util.Collection", + "queryAllDeclaredMethods":true }, { - "name":"java.security.AllPermission" + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true }, { - "name":"java.security.SecurityPermission" + "name":"java.util.List", + "queryAllDeclaredMethods":true }, { - "name":"java.util.PropertyPermission" + "name":"java.util.Map", + "queryAllDeclaredMethods":true }, { - "name":"java.util.concurrent.ForkJoinTask", - "fields":[{"name":"aux"}, {"name":"status"}] + "name":"java.util.RandomAccess", + "queryAllDeclaredMethods":true }, { "name":"java.util.concurrent.atomic.AtomicBoolean", @@ -202,13 +124,7 @@ "fields":[{"name":"value"}] }, { - "name":"javax.smartcardio.CardPermission" -}, -{ - "name":"jdk.internal.misc.Unsafe" -}, -{ - "name":"kotlin.jvm.JvmInline" + "name":"kotlin.Metadata" }, { "name":"org.apiguardian.api.API", @@ -282,9 +198,5 @@ "name":"software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder", "queryAllPublicMethods":true, "methods":[{"name":"<init>","parameterTypes":[] }] -}, -{ - "name":"sun.reflect.ReflectionFactory", - "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] } ] 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 index dea71883a..2fc3c56bd 100644 --- 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 @@ -8,20 +8,10 @@ "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" }, { "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\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":"\\Qlogback-test.scmo\\E" - }, { - "pattern":"\\Qlogback-test.xml\\E" }, { "pattern":"\\Qlogback.scmo\\E" }]}, 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 index 7e8977508..65277e3d6 100644 --- 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 @@ -17,8 +17,6 @@ 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 static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -36,7 +34,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.mockito.Mock; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; @@ -48,16 +46,13 @@ class LambdaEcsEncoderTest { private static final Logger logger = (Logger) LoggerFactory.getLogger(LambdaEcsEncoderTest.class.getName()); - - @Mock private Context context; @BeforeEach void setUp() throws IllegalAccessException, IOException { - openMocks(this); MDC.clear(); writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); + 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(); @@ -67,7 +62,7 @@ void setUp() throws IllegalAccessException, IOException { } @AfterEach - void cleanUp() throws IOException{ + void cleanUp() throws IOException { FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); } @@ -78,7 +73,7 @@ void shouldLogInEcsFormat() { 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\":\"eu-west-1\",\"cloud.account.id\":\"012345678910\",\"faas.id\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"faas.name\":\"testFunction\",\"faas.version\":\"1\",\"faas.memory\":\"1024\",\"faas.execution\":\"RequestId\",\"faas.coldstart\":\"true\",\"trace.id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\"}\n"); + "\"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); @@ -94,7 +89,8 @@ void shouldNotLogFunctionInfo() { String result = new String(encoded, StandardCharsets.UTF_8); // THEN - assertThat(result).contains("\"faas.id\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"faas.name\":\"testFunction\",\"faas.version\":\"1\",\"faas.memory\":\"1024\",\"faas.execution\":\"RequestId\",\"faas.coldstart\":\"false\""); + 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\""); // WHEN (includeFaasInfo = false) encoder.setIncludeFaasInfo(false); @@ -116,7 +112,8 @@ void shouldNotLogCloudInfo() { String result = new String(encoded, StandardCharsets.UTF_8); // THEN - assertThat(result).contains("\"cloud.provider\":\"aws\",\"cloud.service.name\":\"lambda\",\"cloud.region\":\"eu-west-1\",\"cloud.account.id\":\"012345678910\""); + 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); @@ -132,14 +129,16 @@ void shouldLogException() { // GIVEN LambdaEcsEncoder encoder = new LambdaEcsEncoder(); encoder.start(); - LoggingEvent errorloggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Error", new IllegalStateException("Unexpected value"), null); + 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"); + 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(); @@ -150,7 +149,8 @@ void shouldLogException() { 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"); + 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() { @@ -165,12 +165,4 @@ private void setMDC() { MDC.put(PowertoolsLoggedFields.SERVICE.getName(), "Service"); } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn( - "arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(1024); - when(context.getAwsRequestId()).thenReturn("RequestId"); - } } 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 index c0eecf6f3..912e2fde9 100644 --- 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 @@ -19,8 +19,6 @@ 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 static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -29,7 +27,6 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.util.JSONPObject; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -51,7 +48,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.mockito.Mock; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; @@ -69,16 +66,15 @@ @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); - @Mock private Context context; @BeforeEach void setUp() throws IllegalAccessException, IOException { - openMocks(this); MDC.clear(); writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); + 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(); @@ -88,7 +84,7 @@ void setUp() throws IllegalAccessException, IOException { } @AfterEach - void cleanUp() throws IOException{ + void cleanUp() throws IOException { FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); } @@ -103,7 +99,7 @@ void shouldLogInJsonFormat() { // 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:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"function_version\":1,\"service\":\"testLogback\",\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\",\"timestamp\":"); + "{\"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 @@ -114,7 +110,7 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { msg.setMessageId("1212abcd"); msg.setBody("plop"); msg.setEventSource("eb"); - msg.setAwsRegion("eu-west-1"); + msg.setAwsRegion("eu-central-1"); SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); @@ -125,7 +121,8 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .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\\\"\""); @@ -146,7 +143,7 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { msg.setMessageId("1212abcd"); msg.setBody("plop"); msg.setEventSource("eb"); - msg.setAwsRegion("eu-west-1"); + msg.setAwsRegion("eu-central-1"); SQSEvent.MessageAttribute attribute = new SQSEvent.MessageAttribute(); attribute.setStringListValues(Arrays.asList("val1", "val2", "val3")); msg.setMessageAttributes(Collections.singletonMap("keyAttribute", attribute)); @@ -157,7 +154,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"input\":{\"awsRegion\":\"eu-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") + .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\\\"\""); @@ -170,8 +168,6 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { }); } - private final LoggingEvent loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "message", null, null); - @Test void shouldNotLogPowertoolsInfo() { // GIVEN @@ -192,7 +188,8 @@ void shouldNotLogPowertoolsInfo() { String result = new String(encoded, StandardCharsets.UTF_8); // THEN - assertThat(result).contains("{\"level\":\"INFO\",\"message\":\"message\",\"cold_start\":false,\"function_arn\":\"arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1\",\"function_memory_size\":1024,\"function_name\":\"testFunction\",\"function_request_id\":\"RequestId\",\"function_version\":1,\"sampling_rate\":0.2,\"service\":\"Service\",\"timestamp\":"); + 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); @@ -200,7 +197,8 @@ void shouldNotLogPowertoolsInfo() { 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"); + assertThat(result).doesNotContain("cold_start", "function_arn", "function_memory_size", "function_name", + "function_request_id", "function_version", "sampling_rate", "service"); } @Test @@ -212,20 +210,22 @@ void shouldLogStructuredArgumentsAsNewEntries() { msg.setMessageId("1212abcd"); msg.setBody("plop"); msg.setEventSource("eb"); - msg.setAwsRegion("eu-west-1"); + 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 loggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "A message", null, new Object[]{argument}); - byte[] encoded = encoder.encode(loggingEvent); + 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-west-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}"); + .contains( + "\"message\":\"A message\",\"msg\":{\"awsRegion\":\"eu-central-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}"); } @Test @@ -252,7 +252,7 @@ void shouldLogEventForHandlerWhenEnvVariableSetToTrue() { SQSEvent.SQSMessage message = new SQSEvent.SQSMessage(); message.setBody("body"); message.setMessageId("1234abcd"); - message.setAwsRegion("eu-west-1"); + message.setAwsRegion("eu-central-1"); // WHEN requestHandler.handleRequest(message, context); @@ -260,8 +260,9 @@ void shouldLogEventForHandlerWhenEnvVariableSetToTrue() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"message\":\"Handler Event\"") - .contains("\"event\":{\"awsRegion\":\"eu-west-1\",\"body\":\"body\",\"messageId\":\"1234abcd\"}"); + .contains("\"message\":\"Handler Event\"") + .contains( + "\"event\":{\"awsRegion\":\"eu-central-1\",\"body\":\"body\",\"messageId\":\"1234abcd\"}"); } finally { LoggingConstants.POWERTOOLS_LOG_EVENT = false; } @@ -288,7 +289,10 @@ void shouldLogEventAsStringForStreamHandler() throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); // WHEN - requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(Collections.singletonMap("key", "value"))), output, context); + requestStreamHandler.handleRequest( + new ByteArrayInputStream( + new ObjectMapper().writeValueAsBytes(Collections.singletonMap("key", "value"))), + output, context); // THEN assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) @@ -345,7 +349,8 @@ void shouldLogResponseForStreamHandler() throws IOException { String input = "<user><firstName>Bob</firstName><lastName>The Sponge</lastName></user>"; // WHEN - requestStreamHandler.handleRequest(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), output, context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), output, + context); // THEN assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) @@ -353,8 +358,8 @@ void shouldLogResponseForStreamHandler() throws IOException { File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)) - .contains("\"message\":\"Handler Response\"") - .contains("\"response\":\""+input+"\""); + .contains("\"message\":\"Handler Response\"") + .contains("\"response\":\"" + input + "\""); } @Test @@ -368,7 +373,8 @@ void shouldLogThreadInfo() { String result = new String(encoded, StandardCharsets.UTF_8); // THEN - assertThat(result).contains("\"thread\":\"main\",\"thread_id\":"+ Thread.currentThread().getId() +",\"thread_priority\":5"); + assertThat(result).contains( + "\"thread\":\"main\",\"thread_id\":" + Thread.currentThread().getId() + ",\"thread_priority\":5"); } @Test @@ -388,7 +394,7 @@ void shouldLogTimestampDifferently() { // THEN SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); simpleDateFormat.setTimeZone(TimeZone.getTimeZone(timeZone)); - assertThat(result).contains("\"timestamp\":\""+simpleDateFormat.format(date)+"\""); + assertThat(result).contains("\"timestamp\":\"" + simpleDateFormat.format(date) + "\""); } @Test @@ -396,7 +402,8 @@ void shouldLogException() { // GIVEN LambdaJsonEncoder encoder = new LambdaJsonEncoder(); encoder.start(); - LoggingEvent errorloggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Error", new IllegalStateException("Unexpected value"), null); + LoggingEvent errorloggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Error", + new IllegalStateException("Unexpected value"), null); // WHEN byte[] encoded = encoder.encode(errorloggingEvent); @@ -406,7 +413,8 @@ void shouldLogException() { 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"); + .contains( + "\"stack\":\"[software.amazon.lambda.powertools.logging.internal.LambdaJsonEncoderTest.shouldLogException"); // WHEN (configure a custom throwableConverter) encoder = new LambdaJsonEncoder(); @@ -422,12 +430,4 @@ void shouldLogException() { .contains("\"stack\":\"java.lang.IllegalStateException: Unexpected value\\n"); } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn( - "arn:aws:lambda:eu-west-1:012345678910:function:testFunction:1"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(1024); - when(context.getAwsRequestId()).thenReturn("RequestId"); - } } 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 aba4664fe..751d195b5 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 @@ -18,8 +18,6 @@ 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 static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; 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; @@ -54,7 +52,6 @@ import org.junitpioneer.jupiter.ClearEnvironmentVariable; import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.junitpioneer.jupiter.SetSystemProperty; -import org.mockito.Mock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -72,6 +69,7 @@ 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.argument.StructuredArgument; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId; @@ -99,15 +97,13 @@ class LambdaLoggingAspectTest { private RequestStreamHandler requestStreamHandler; private RequestHandler<Object, Object> requestHandler; - @Mock private Context context; @BeforeEach void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException { - openMocks(this); MDC.clear(); writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); + context = new TestLambdaContext(); requestHandler = new PowertoolsLogEnabled(); requestStreamHandler = new PowertoolsLogEnabledForStream(); resetLogLevel(Level.INFO); @@ -124,7 +120,7 @@ void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTar @AfterEach void cleanUp() throws IOException { - //Make sure file is cleaned up before running full stack logging regression + // Make sure file is cleaned up before running full stack logging regression FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); } @@ -224,7 +220,8 @@ void shouldLogWarnWhenPowertoolsLevelEnvVarIsWarnAndLambdaLevelVarIsInfo() throw 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"); + assertThat(contentOf(logFile)) + .doesNotContain(" does not match AWS Lambda Advanced Logging Controls minimum log level"); } @Test @@ -241,7 +238,8 @@ void shouldLogInfoWhenPowertoolsLevelEnvVarIsInfoAndLambdaLevelVarIsWarn() throw 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."); + 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 @@ -265,11 +263,11 @@ void shouldSetLambdaContextWhenEnabled() { assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry(FUNCTION_ARN.getName(), "testArn") - .containsEntry(FUNCTION_MEMORY_SIZE.getName(), "10") + .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(), "testFunction") - .containsEntry(FUNCTION_REQUEST_ID.getName(), "RequestId") + .containsEntry(FUNCTION_NAME.getName(), "test-function") + .containsEntry(FUNCTION_REQUEST_ID.getName(), "test-request-id") .containsKey(FUNCTION_COLD_START.getName()) .containsKey(SERVICE.getName()); } @@ -278,30 +276,30 @@ void shouldSetLambdaContextWhenEnabled() { void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { requestStreamHandler = new PowertoolsLogEnabledForStream(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) - .containsEntry(FUNCTION_ARN.getName(), "testArn") - .containsEntry(FUNCTION_MEMORY_SIZE.getName(), "10") + .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(), "testFunction") - .containsEntry(FUNCTION_REQUEST_ID.getName(), "RequestId") + .containsEntry(FUNCTION_NAME.getName(), "test-function") + .containsEntry(FUNCTION_REQUEST_ID.getName(), "test-request-id") .containsKey(FUNCTION_COLD_START.getName()) .containsKey(SERVICE.getName()); } @Test void shouldSetColdStartFlagOnFirstCallNotOnSecondCall() throws IOException { - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); assertThat(MDC.getCopyOfContextMap()) .hasSize(EXPECTED_CONTEXT_SIZE) .containsEntry(FUNCTION_COLD_START.getName(), "true"); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); assertThat(MDC.getCopyOfContextMap()) @@ -346,7 +344,7 @@ void shouldLogDebugWhenSamplingEqualsOne() { } @Test - void shouldLogDebugWhenSamplingEnvVarEqualsOne() throws IllegalAccessException { + void shouldLogDebugWhenSamplingEnvVarEqualsOne() { // GIVEN LoggingConstants.POWERTOOLS_SAMPLING_RATE = "1"; PowertoolsLogEnabled handler = new PowertoolsLogEnabled(); @@ -360,7 +358,7 @@ void shouldLogDebugWhenSamplingEnvVarEqualsOne() throws IllegalAccessException { } @Test - void shouldNotLogDebugWhenSamplingEnvVarIsTooBig() throws IllegalAccessException { + void shouldNotLogDebugWhenSamplingEnvVarIsTooBig() { // GIVEN LoggingConstants.POWERTOOLS_SAMPLING_RATE = "42"; @@ -378,7 +376,7 @@ void shouldNotLogDebugWhenSamplingEnvVarIsInvalid() { LoggingConstants.POWERTOOLS_SAMPLING_RATE = "NotANumber"; // WHEN - requestHandler.handleRequest(new Object(), context); + requestHandler.handleRequest(new Object(), context); // THEN File logFile = new File("target/logfile.json"); @@ -509,7 +507,7 @@ void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() { requestHandler.handleRequest(singletonList("ListOfOneElement"), context); // THEN - TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar)requestHandler).getLogger(); + TestLogger logger = (TestLogger) ((PowertoolsLogEventEnvVar) requestHandler).getLogger(); assertThat(logger.getArguments()).isNull(); } @@ -521,7 +519,8 @@ void shouldLogEventForStreamHandler() throws IOException { Map<String, String> map = Collections.singletonMap("key", "value"); // WHEN - requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(map)), output, context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(map)), output, + context); // THEN assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) @@ -586,7 +585,8 @@ void shouldLogResponseForStreamHandler() throws IOException { String input = "<user><firstName>Bob</firstName><lastName>The Sponge</lastName></user>"; // WHEN - requestStreamHandler.handleRequest(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), output, context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), output, + context); // THEN assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) @@ -737,7 +737,8 @@ void testMultipleLoggingManagers_shouldWarnAndSelectFirstOne() throws Unsupporte 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 to your dependencies") + .contains( + "WARN. Make sure to have only one of powertools-logging-log4j OR powertools-logging-logback to your dependencies") .contains("WARN. Using the first LoggingManager found on the classpath: [" + list.get(0) + "]"); } @@ -757,19 +758,12 @@ void testNoLoggingManagers_shouldWarnAndCreateDefault() throws UnsupportedEncodi 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"); + .contains( + "ERROR. Make sure to add either powertools-logging-log4j or powertools-logging-logback to your dependencies"); assertThat(loggingManager).isExactlyInstanceOf(DefautlLoggingManager.class); } - 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 setLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("setLogLevels", Level.class); diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 57da447dd..4bccab505 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -85,16 +85,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-junit-jupiter</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> @@ -115,6 +105,13 @@ <artifactId>assertj-core</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> @@ -126,7 +123,8 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>-Xlog:class+load=info:classesloaded.txt + <argLine> + -Xlog:class+load=info:classesloaded.txt --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED </argLine> @@ -137,20 +135,14 @@ </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>-Dmockito.mock.maker=subclass -Dorg.graalvm.nativeimage.imagecode=agent + <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 @@ -162,13 +154,6 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -191,40 +176,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg> - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> 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 index 1bf3b6a69..ecc7c13a1 100644 --- 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 @@ -34,7 +34,7 @@ import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.model.MetricUnit; -import software.amazon.lambda.powertools.metrics.testutils.TestContext; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; /** * Tests to verify the hierarchy of precedence for configuration: @@ -89,7 +89,7 @@ void annotationShouldOverrideBuilderAndEnvironment() throws Exception { .build(); RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -118,7 +118,7 @@ void builderShouldOverrideEnvironment() throws Exception { .build(); RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -141,7 +141,7 @@ void builderShouldOverrideEnvironment() throws Exception { void environmentVariablesShouldBeUsedWhenNoOverrides() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -166,7 +166,7 @@ void shouldUseDefaultsWhenNoConfiguration() throws Exception { .build(); RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When 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 index a4fc0d61c..6c324221c 100644 --- 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 @@ -45,7 +45,7 @@ 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.testutils.TestContext; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; class EmfMetricsLoggerTest { @@ -373,7 +373,7 @@ void shouldClearDefaultDimensions() throws Exception { @Test void shouldCaptureColdStartMetric() throws Exception { // Given - Context testContext = new TestContext(); + Context testContext = new TestLambdaContext(); // When metrics.captureColdStartMetric(testContext); @@ -425,7 +425,7 @@ void shouldReuseNamespaceForColdStartMetric() throws Exception { String customNamespace = "CustomNamespace"; metrics.setNamespace(customNamespace); - Context testContext = new TestContext(); + Context testContext = new TestLambdaContext(); DimensionSet dimensions = DimensionSet.of("CustomDim", "CustomValue"); @@ -497,7 +497,7 @@ void shouldNotFlushMetricsWhenDisabled() { @SetEnvironmentVariable(key = "POWERTOOLS_METRICS_DISABLED", value = "true") void shouldNotCaptureColdStartMetricWhenDisabled() { // Given - Context testContext = new TestContext(); + Context testContext = new TestLambdaContext(); // When metrics.captureColdStartMetric(testContext); 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 068d19ccb..326dd2ffe 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 @@ -37,7 +37,7 @@ 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.metrics.testutils.TestContext; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; class LambdaMetricsAspectTest { @@ -74,7 +74,7 @@ void tearDown() throws Exception { void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -98,7 +98,7 @@ void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { void shouldOverrideEnvironmentVariablesWithAnnotation() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -120,7 +120,7 @@ void shouldOverrideEnvironmentVariablesWithAnnotation() throws Exception { void shouldUseEnvironmentVariablesWhenNoAnnotationOverrides() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -140,7 +140,7 @@ void shouldUseEnvironmentVariablesWhenNoAnnotationOverrides() throws Exception { void shouldCaptureColdStartMetricWhenConfigured() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithColdStartMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -166,7 +166,7 @@ void shouldCaptureColdStartMetricWhenConfigured() throws Exception { 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 TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -195,7 +195,7 @@ void shouldNotIncludeServiceDimensionInColdStartMetricWhenServiceUndefined() thr void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithCustomFunctionName(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -228,7 +228,7 @@ void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exceptio void shouldUseServiceNameWhenProvidedForColdStartMetric() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithServiceNameAndColdStart(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When @@ -247,7 +247,7 @@ void shouldUseServiceNameWhenProvidedForColdStartMetric() throws Exception { void shouldHaveNoEffectOnNonHandlerMethod() { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithAnnotationOnWrongMethod(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map<String, Object> input = new HashMap<>(); // When diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index d43221b5e..617e0cc00 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/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> <parent> @@ -43,11 +43,11 @@ <groupId>software.amazon.awssdk</groupId> <artifactId>url-connection-client</artifactId> </dependency> - <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> + <!-- Test dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> @@ -93,14 +93,6 @@ <profiles> <profile> <id>generate-graalvm-files</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -108,7 +100,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -119,20 +113,12 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -149,33 +135,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 4d650c58b..ed92930fc 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -57,6 +57,11 @@ <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> @@ -86,7 +91,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -97,7 +101,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -112,7 +118,13 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> + <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> @@ -121,7 +133,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -138,33 +150,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> 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 index f50e88ec5..df3191632 100644 --- 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 @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.function.BiFunction; + import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java index da01d7d9a..7f06ed412 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderTest.java @@ -18,15 +18,16 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; 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 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; @@ -37,6 +38,7 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +@ExtendWith(MockitoExtension.class) class AppConfigProviderTest { private static final String ENVIRONMENT_NAME = "test"; @@ -55,8 +57,6 @@ class AppConfigProviderTest { @BeforeEach void init() { - openMocks(this); - provider = AppConfigProvider.builder() .withClient(client) .withApplication(APPLICATION_NAME) diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 1834b229d..b8857b0c9 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -58,6 +58,11 @@ <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> @@ -87,7 +92,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -98,7 +102,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -113,7 +119,13 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> + <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> @@ -122,7 +134,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -139,33 +151,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 10d756c69..68d48b01c 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -23,13 +23,17 @@ 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; @@ -40,17 +44,23 @@ import software.amazon.lambda.powertools.parameters.dynamodb.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +@ExtendWith(MockitoExtension.class) public class DynamoDbProviderTest { 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 @@ -60,7 +70,6 @@ public void init() { provider = new DynamoDbProvider(cacheManager, transformationManager, client, tableName); } - @Test public void getValue() { @@ -84,7 +93,6 @@ public void getValue() { assertThat(getItemValueCaptor.getValue().key().get("id").s()).isEqualTo(key); } - @Test public void getValueWithNullResultsReturnsNull() { // Arrange @@ -124,13 +132,11 @@ public void getValueWithMalformedRowThrows() { .item(responseData) .build()); // Act - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { provider.getValue(key); }); } - @Test public void getValues() { @@ -191,8 +197,7 @@ 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); }); @@ -212,8 +217,7 @@ public void getValuesWithMalformedRowThrows() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> - { + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { // Act provider.getMultipleValues(key); }); @@ -227,6 +231,7 @@ public void testDynamoDBBuilderMissingTable_throwsException() { .withCacheManager(new CacheManager()) .build()); } + @Test public void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { @@ -234,7 +239,7 @@ public void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformation DynamoDbProvider dynamoDbProvider = DynamoDbProvider.builder().withTable("test-table") .build(); // Assert - assertDoesNotThrow(()->dynamoDbProvider.withTransformation(json)); + assertDoesNotThrow(() -> dynamoDbProvider.withTransformation(json)); } } diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index ef032f0c3..b486a1bb3 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -58,6 +58,11 @@ <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> @@ -87,7 +92,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -98,7 +102,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -113,7 +119,13 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> + <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> @@ -122,7 +134,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -139,33 +151,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java index a2607dd2c..d0b32874a 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java @@ -25,11 +25,13 @@ 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.MockitoAnnotations; +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; @@ -37,6 +39,7 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +@ExtendWith(MockitoExtension.class) public class SecretsProviderTest { @Mock @@ -54,7 +57,6 @@ public class SecretsProviderTest { @BeforeEach public void init() { - MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); provider = new SecretsProvider(cacheManager, transformationManager, client); } @@ -78,8 +80,8 @@ public 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); @@ -90,16 +92,13 @@ public void getValueBase64() { @Test public void getMultipleValuesThrowsException() { - // Act & Assert assertThatRuntimeException().isThrownBy(() -> provider.getMultipleValues("path")) .withMessage("Impossible to get multiple values from AWS Secrets Manager"); - } @Test public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { - // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); @@ -111,11 +110,10 @@ public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() @Test public void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { - // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); // Assert - assertDoesNotThrow(()->secretsProvider.withTransformation(json)); + assertDoesNotThrow(() -> secretsProvider.withTransformation(json)); } } diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index fcb7b5311..709ea6634 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -58,6 +58,11 @@ <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> @@ -101,7 +106,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -112,7 +116,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -127,7 +133,13 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> + <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> @@ -136,7 +148,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -153,33 +165,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> @@ -187,4 +176,4 @@ </build> </profile> </profiles> -</project> \ No newline at end of file +</project> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index abd2a02d2..6dcf2c4ae 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -1,7 +1,7 @@ <?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"> + 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> @@ -50,6 +50,11 @@ <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> @@ -97,7 +102,6 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> <scope>test</scope> </dependency> </dependencies> @@ -108,7 +112,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -123,7 +129,13 @@ <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> + <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> @@ -132,7 +144,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -149,33 +161,10 @@ <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> diff --git a/powertools-parameters/powertools-parameters-tests/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 index 5fa740253..cbc8f5b30 100644 --- a/powertools-parameters/powertools-parameters-tests/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,7 +20,6 @@ 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; @@ -30,8 +29,10 @@ 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; @@ -43,13 +44,10 @@ public class BaseProviderTest { CacheManager cacheManager; TransformationManager transformationManager; BasicProvider provider; - boolean getFromStore = false; @BeforeEach public void setup() { - openMocks(this); - clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); transformationManager = new TransformationManager(); @@ -197,8 +195,8 @@ public void get_basicTransformation_shouldTransformInString() { public 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") diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java index 7d790d140..6b3cf7641 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java @@ -19,17 +19,19 @@ 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 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 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,27 +45,26 @@ import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider; import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; +@ExtendWith(MockitoExtension.class) public class ParamProvidersIntegrationTest { - @Mock SsmClient ssmClient; @Mock DynamoDbClient ddbClient; + @Captor ArgumentCaptor<GetParameterRequest> ssmParamCaptor; + @Captor ArgumentCaptor<GetParametersByPathRequest> ssmParamByPathCaptor; + @Mock SecretsManagerClient secretsManagerClient; + @Captor ArgumentCaptor<GetSecretValueRequest> secretsCaptor; - @BeforeEach - public void setup() throws IllegalAccessException { - openMocks(this); - } - @Test public void ssmProvider_get() { SSMProvider ssmProvider = SSMProvider.builder() diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index d6cc33891..7e4e2af15 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/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> <parent> @@ -54,6 +54,11 @@ <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> @@ -93,14 +98,6 @@ <profiles> <profile> <id>generate-graalvm-files</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -108,7 +105,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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> + <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> @@ -116,20 +115,12 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -141,37 +132,15 @@ </execution> </executions> <configuration> - <agent> - <enabled>true</enabled> - <defaultMode>Standard</defaultMode> - </agent> <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>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> + <buildArg>--native-image-info</buildArg> + <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index cb7f8218f..67de0be7d 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> @@ -87,8 +87,10 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> <scope>test</scope> </dependency> <dependency> @@ -121,14 +123,6 @@ <profiles> <profile> <id>generate-graalvm-files</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> @@ -136,7 +130,9 @@ <artifactId>maven-surefire-plugin</artifactId> <version>3.5.3</version> <configuration> - <argLine>-Dmockito.mock.maker=subclass -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 + <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> @@ -147,20 +143,12 @@ </profile> <profile> <id>graalvm-native</id> - <dependencies> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-subclass</artifactId> - <version>5.18.0</version> - <scope>test</scope> - </dependency> - </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> <!-- or newer version --> + <version>0.11.0</version> <extensions>true</extensions> <executions> <execution> @@ -178,35 +166,10 @@ <buildArg>--add-opens java.base/java.lang=ALL-UNNAMED</buildArg> <buildArg>--enable-url-protocols=http</buildArg> <buildArg>--no-fallback</buildArg> - <buildArg>-Dorg.graalvm.nativeimage.imagecode=agent</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun</buildArg> - <buildArg>-H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun</buildArg> - <buildArg>-H:IncludeResources=version.properties</buildArg> - <buildArg>-H:IncludeResources=unreadable.properties</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.simple.SimpleLogger</buildArg> - <buildArg>--initialize-at-build-time=org.slf4j.LoggerFactory</buildArg> - <buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg> - <buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg> - <buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg> - <buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg> - <buildArg> - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - </buildArg> <buildArg>--verbose</buildArg> <buildArg>--native-image-info</buildArg> <buildArg>-H:+UnlockExperimentalVMOptions</buildArg> - <buildArg>-H:Log=registerResource:5</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> </buildArgs> </configuration> </plugin> 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 01f25f37a..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,17 +16,15 @@ 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; +import com.amazonaws.xray.AWSXRay; +import com.amazonaws.xray.entities.Entity; + class TracingUtilsTest { @BeforeEach void setUp() { @@ -55,8 +53,7 @@ void shouldSetAnnotationOnCurrentSubSegment() { .contains( entry("stringKey", "val"), entry("numberKey", 10), - entry("booleanKey", false) - ); + entry("booleanKey", false)); } @Test @@ -76,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 @@ -92,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"); @@ -142,21 +130,14 @@ void shouldAddAnnotationIfValidCharactersInKey() { @Test void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { - Context test = mock(Context.class); - - TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> - { + 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"); @@ -174,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"); @@ -207,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"); 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 1d108ed5f..4d2e481b1 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,8 +18,6 @@ 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.when; -import static org.mockito.MockitoAnnotations.openMocks; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -29,7 +27,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.SetEnvironmentVariable; -import org.mockito.Mock; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -37,6 +34,7 @@ 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; @@ -50,7 +48,6 @@ import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaData; 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 { @@ -58,14 +55,12 @@ class LambdaTracingAspectTest { private RequestStreamHandler streamHandler; private PowerToolNonHandler nonHandlerMethod; - @Mock private Context context; @BeforeEach void setUp() throws IllegalAccessException { - openMocks(this); writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); - setupContext(); + context = new TestLambdaContext(); requestHandler = new PowerTracerToolEnabled(); streamHandler = new PowerTracerToolEnabledForStream(); nonHandlerMethod = new PowerToolNonHandler(); @@ -86,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 @@ -99,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 @@ -112,15 +105,14 @@ 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()) - .hasSize(0); + .isEmpty(); }); } @@ -134,8 +126,7 @@ void shouldCaptureTracesWithResponseMetadata() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -159,8 +150,7 @@ void shouldCaptureTracesWithExceptionMetaData() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -185,8 +175,7 @@ void shouldCaptureTracesForStream() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -204,8 +193,7 @@ void shouldCaptureTracesForStreamWithResponseMetadata() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -246,8 +234,7 @@ void shouldCaptureTracesWithNoMetadata() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -269,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) @@ -291,8 +277,7 @@ void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -315,8 +300,7 @@ void shouldCaptureTracesIfExplicitlyEnabledAndEnvironmentVariableIsDisabled() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -339,8 +323,7 @@ void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getMetadata()) .hasSize(1) .containsKey("lambdaHandler"); @@ -368,8 +351,7 @@ void shouldCaptureTracesIfExplicitlyEnabledBothAndEnvironmentVariableIsDisabled( assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -396,8 +378,7 @@ void shouldNotCaptureTracesWithExceptionMetaDataIfDisabledViaEnvironmentVariable assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -420,8 +401,7 @@ void shouldCaptureTracesWithExceptionMetaDataEnabledExplicitlyAndEnvironmentVari assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> - { + .allSatisfy(subsegment -> { assertThat(subsegment.getAnnotations()) .hasSize(2) .containsEntry("ColdStart", true) @@ -437,10 +417,4 @@ void shouldCaptureTracesWithExceptionMetaDataEnabledExplicitlyAndEnvironmentVari }); } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(10); - } } From 5647e0d4c64e96e7f582b9a7fc19b1182eb54c8b Mon Sep 17 00:00:00 2001 From: ARYAN RAJ <nikhilaryan0928@gmail.com> Date: Mon, 25 Aug 2025 19:55:29 +0530 Subject: [PATCH 0818/1008] chore(gitignore): add .kiro, .claude, .amazonq to prevent deletion (#2078) * add: .kiro, .claude, .amazonq to .gitignore to prevent deletion * docs: add .github/instructions explaining ignored folders * chore: remove .github/instructions * chore(gitignore): add .github/instructions to ignore local instructions file * Update .gitignore Co-authored-by: Philipp Page <philipp.page@yahoo.de> * Update .gitignore Co-authored-by: Philipp Page <philipp.page@yahoo.de> --------- Co-authored-by: Andrea Amorosi <dreamorosi@gmail.com> Co-authored-by: Philipp Page <philipp.page@yahoo.de> --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 0972d7449..eb2ea4f18 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,10 @@ example/HelloWorldFunction/build build/ .terraform* terraform.tfstate* + +# LLMs +.kiro/ +.claude/ +.amazonq/ +.github/instructions + From f273dcc89f10a25b091e1ac169495fa31c2c0489 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 08:56:44 -0300 Subject: [PATCH 0819/1008] chore: bump aws.sdk.version from 2.32.28 to 2.32.30 (#2089) Bumps `aws.sdk.version` from 2.32.28 to 2.32.30. Updates `software.amazon.awssdk:bom` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:http-client-spi` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:url-connection-client` from 2.32.26 to 2.32.30 Updates `software.amazon.awssdk:dynamodb` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:s3` from 2.32.26 to 2.32.30 Updates `software.amazon.awssdk:lambda` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:kinesis` from 2.32.26 to 2.32.30 Updates `software.amazon.awssdk:cloudwatch` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:xray` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:sqs` from 2.32.26 to 2.32.30 Updates `software.amazon.awssdk:cloudformation` from 2.32.28 to 2.32.30 Updates `software.amazon.awssdk:sts` from 2.32.28 to 2.32.30 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.30 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.30 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.30 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index cfb757440..b92f458b0 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.28</aws.sdk.version> + <aws.sdk.version>2.32.30</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index f6ccb5056..f2e9b1cdf 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.28</aws.sdk.version> + <aws.sdk.version>2.32.30</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 8200b6ca3..8cc580958 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -20,7 +20,7 @@ <maven.shade.version>3.6.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.32.28</aws.sdk.version> + <aws.sdk.version>2.32.30</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> From 7594e5ae4b35c113ea8c82188b206fbe91df78c4 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 27 Aug 2025 15:21:02 +0200 Subject: [PATCH 0820/1008] feat(graalvm): GraalVM support for Idempotency utility (#2080) * Add Idempotency GraalVM example and remove unncessary dependencies from pom.xml * Add native maven profiles to idempotency-core. * Configure GraalVM profile for idempotency-dynamodb. Refactor unit tests to treat DynamoDBLocal as external process instead of starting it via JVM which is not compatible with GraalVM. * Add GRM data for idempotency dynamodb. * Remove verbose register resource logs. * Remove redundant APIGatewayProxyResponseEvent reflect metadata. * Remove unncessary includeGroupIds. * Add back Log4j2PluginCacheFileTransformer * Address sonar findings. * Fix sonar findings. * Use aws-powertools_ubuntu-latest_8-core for graalvm-build. * Enable Idempotency E2E test for GraalVM. * Add GraalVM support to Idempotency E2E test handler. * Add required Lambda dependencies for native images. * Increase Idempotency E2E test timeout. * Add --platform linux/amd64 to bundling options. --- .github/workflows/check-build.yml | 2 +- examples/README.md | 30 +- examples/pom.xml | 3 +- .../sam-graalvm/Dockerfile | 14 + .../sam-graalvm/Makefile | 5 + .../sam-graalvm/README.md | 61 ++++ .../sam-graalvm/pom.xml | 167 +++++++++ .../sam-graalvm/src/main/config/bootstrap | 4 + .../src/main/java/helloworld/App.java | 0 .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 30 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 34 ++ .../resource-config.json | 19 + .../reflect-config.json | 25 ++ .../helloworld/reflect-config.json | 11 + .../helloworld/resource-config.json | 7 + .../src/main/resources/log4j2.xml | 0 .../sam-graalvm/template.yaml | 60 ++++ .../{ => sam}/README.md | 0 .../{ => sam}/pom.xml | 54 +-- .../sam/src/main/java/helloworld/App.java | 142 ++++++++ .../sam/src/main/resources/log4j2.xml | 16 + .../{ => sam}/template.yaml | 0 pom.xml | 5 + .../handlers/idempotency/pom.xml | 24 +- .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 ++++ .../resource-config.json | 19 + .../reflect-config.json | 25 ++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + powertools-e2e-tests/pom.xml | 1 + .../lambda/powertools/IdempotencyE2ET.java | 2 +- .../testutils/DockerConfiguration.java | 2 + .../powertools-idempotency-core/pom.xml | 95 ++++- .../internal/IdempotencyAspectTest.java | 68 ++-- .../internal/cache/LRUCacheTest.java | 8 +- .../persistence/BasePersistenceStoreTest.java | 168 ++++----- .../test/resources/simplelogger.properties | 7 + .../dynamodb-local-metadata.json | 1 - .../powertools-idempotency-dynamodb/pom.xml | 164 ++++++++- .../reflect-config.json | 325 ++++++++++++++++++ .../resource-config.json | 27 ++ .../persistence/dynamodb/DynamoDBConfig.java | 69 +--- .../DynamoDBPersistenceStoreTest.java | 192 +++++------ .../persistence/dynamodb/IdempotencyTest.java | 16 +- .../test/resources/simplelogger.properties | 7 + 52 files changed, 1697 insertions(+), 385 deletions(-) create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/Dockerfile create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/Makefile create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/README.md create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/pom.xml create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/config/bootstrap rename examples/powertools-examples-idempotency/{ => sam-graalvm}/src/main/java/helloworld/App.java (100%) create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/reflect-config.json create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/helloworld/resource-config.json rename examples/powertools-examples-idempotency/{ => sam-graalvm}/src/main/resources/log4j2.xml (100%) create mode 100644 examples/powertools-examples-idempotency/sam-graalvm/template.yaml rename examples/powertools-examples-idempotency/{ => sam}/README.md (100%) rename examples/powertools-examples-idempotency/{ => sam}/pom.xml (71%) create mode 100644 examples/powertools-examples-idempotency/sam/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml rename examples/powertools-examples-idempotency/{ => sam}/template.yaml (100%) create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/resources/simplelogger.properties delete mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/reflect-config.json create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-dynamodb/resource-config.json create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/src/test/resources/simplelogger.properties diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 98e1eae6e..3abb1440c 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -87,7 +87,7 @@ jobs: mvn -B -q install --file pom.xml graalvm-build: - runs-on: ubuntu-latest + runs-on: aws-powertools_ubuntu-latest_8-core steps: - id: checkout name: Checkout repository diff --git a/examples/README.md b/examples/README.md index 41640b5ad..727bc652e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,26 +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-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) - * [Gradle](./powertools-examples-core-utilities/gradle) * [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 + * [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. @@ -29,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 @@ -56,7 +61,7 @@ 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 @@ -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 2c561b00f..554e97168 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -35,7 +35,8 @@ <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</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> 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..6abb743f9 --- /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.3.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.3.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.3</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.0</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.0</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 100% 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 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..10152cc64 --- /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,34 @@ +[ + { + "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 + } +] \ 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/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/src/main/resources/log4j2.xml b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-idempotency/src/main/resources/log4j2.xml rename to examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/log4j2.xml 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/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml similarity index 71% rename from examples/powertools-examples-idempotency/pom.xml rename to examples/powertools-examples-idempotency/sam/pom.xml index eb235437b..a5f49d9c6 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -13,7 +13,7 @@ --> <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> @@ -44,6 +44,7 @@ <artifactId>powertools-idempotency-dynamodb</artifactId> <version>${project.version}</version> </dependency> + <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> @@ -54,24 +55,12 @@ <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>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> </dependencies> <build> @@ -114,39 +103,7 @@ </dependency> </dependencies> </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.5.3</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> @@ -160,7 +117,8 @@ <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> - <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> </transformers> </configuration> </execution> 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/pom.xml b/pom.xml index f2e9b1cdf..c63e78d7e 100644 --- a/pom.xml +++ b/pom.xml @@ -459,6 +459,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.5.1</version> + </plugin> </plugins> </pluginManagement> <plugins> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 8b9b015bc..b9555ee47 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -1,5 +1,5 @@ <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> @@ -29,6 +29,14 @@ <groupId>org.aspectj</groupId> <artifactId>aspectjrt</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> @@ -65,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/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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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/pom.xml b/powertools-e2e-tests/pom.xml index 15ac7650e..4094bfb20 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -253,6 +253,7 @@ <include>**/LoggingE2ET.java</include> <include>**/ParametersE2ET.java</include> <include>**/TracingE2ET.java</include> + <include>**/IdempotencyE2ET.java</include> </includes> <systemPropertyVariables> <graalvm.enabled>true</graalvm.enabled> 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 292f46bfa..c73a6d761 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 @@ -36,7 +36,7 @@ class IdempotencyE2ET { private static String functionName; @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) + @Timeout(value = 15, unit = TimeUnit.MINUTES) static void setup() { String random = UUID.randomUUID().toString().substring(0, 6); infrastructure = Infrastructure.builder() 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 index 204d5863a..0508534c2 100644 --- 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 @@ -88,6 +88,7 @@ public BundlingOptions createGraalVMBundlingOptions(String pathToFunction, JavaR .environment(environmentVariables) .user("root") .outputType(BundlingOutput.ARCHIVED) + .platform("linux/amd64") .build(); } @@ -111,6 +112,7 @@ public BundlingOptions createJVMBundlingOptions(String pathToFunction, JavaRunti .volumes(volumes) .user("root") .outputType(BundlingOutput.ARCHIVED) + .platform("linux/amd64") .build(); } diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 6f15bfc3d..2f575d3de 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -37,16 +37,109 @@ <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> -</project> \ No newline at end of file + + <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.0</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/powertools-idempotency-core/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 index 12113fc9e..0ccb1e5aa 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -24,23 +24,23 @@ 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 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; @@ -61,21 +61,16 @@ 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 - public void firstCall_shouldPutInStore() { + void firstCall_shouldPutInStore() { Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() @@ -85,8 +80,6 @@ public void firstCall_shouldPutInStore() { 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); @@ -107,12 +100,13 @@ public void firstCall_shouldPutInStore() { } @Test - public void firstCall_shouldPutInStoreAndNotApplyResponseHook() { + void firstCall_shouldPutInStoreAndNotApplyResponseHook() { Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder() .withEventKeyJMESPath("id") - // This hook will add a second product to the basket. It should not run here. Only for + // 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; @@ -124,8 +118,6 @@ public void firstCall_shouldPutInStoreAndNotApplyResponseHook() { 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); // Size should be 1 because response hook should not run @@ -146,7 +138,7 @@ public void firstCall_shouldPutInStoreAndNotApplyResponseHook() { } @Test - public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { + void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -177,7 +169,7 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce } @Test - public void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyException() + void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyException() throws JsonProcessingException { // GIVEN Idempotency.config() @@ -196,12 +188,13 @@ public void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyExc JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), null); - // A data record on this exception should take precedence over fetching a record from the store / cache + // 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(store).saveInProgress(any(), any(), any()); // WHEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -210,12 +203,13 @@ public void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyExc // THEN assertThat(basket).isEqualTo(b); assertThat(function.handlerCalled()).isFalse(); - // Should never call the store because item is already present on IdempotencyItemAlreadyExistsException + // 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) @@ -245,7 +239,7 @@ public void secondCall_notExpired_shouldGetStringFromStore() { } @Test - public void secondCall_notExpired_shouldGetStringFromStoreWithResponseHook() { + void secondCall_notExpired_shouldGetStringFromStoreWithResponseHook() { // GIVEN final String RESPONSE_HOOK_SUFFIX = " ResponseHook"; Idempotency.config() @@ -280,7 +274,7 @@ public void secondCall_notExpired_shouldGetStringFromStoreWithResponseHook() { } @Test - public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() + void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressException() throws JsonProcessingException { // GIVEN Idempotency.config() @@ -312,7 +306,7 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti } @Test - public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() + void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() throws JsonProcessingException { // GIVEN Idempotency.config() @@ -344,7 +338,7 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons } @Test - public void functionThrowException_shouldDeleteRecord_andThrowFunctionException() { + void functionThrowException_shouldDeleteRecord_andThrowFunctionException() { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -365,7 +359,7 @@ 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) @@ -386,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); @@ -416,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(); @@ -444,7 +436,7 @@ public void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shoul } @Test - public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() + void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { // GIVEN Idempotency.config() @@ -473,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() @@ -495,7 +487,7 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey( } @Test - public void idempotencyOnSubMethodAnnotated_keyJMESPathArray_shouldPutInStoreWithKey() { + void idempotencyOnSubMethodAnnotated_keyJMESPathArray_shouldPutInStoreWithKey() { BasePersistenceStore persistenceStore = spy(BasePersistenceStore.class); Idempotency.config() @@ -517,7 +509,7 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPathArray_shouldPutInStoreWit } @Test - public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { + void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder().build()).configure(); @@ -532,7 +524,7 @@ public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { } @Test - public void idempotencyOnSubMethodVoid_shouldThrowException() { + void idempotencyOnSubMethodVoid_shouldThrowException() { Idempotency.config() .withPersistenceStore(store) .withConfig(IdempotencyConfig.builder().build()).configure(); diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 053b56430..d14f07315 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -14,14 +14,14 @@ package software.amazon.lambda.powertools.idempotency.internal.cache; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; -public class LRUCacheTest { +import org.junit.jupiter.api.Test; + +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/powertools-idempotency-core/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 index f770679ee..d5d45c78f 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -14,13 +14,23 @@ package software.amazon.lambda.powertools.idempotency.persistence; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +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.node.DoubleNode; import com.fasterxml.jackson.databind.node.TextNode; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; @@ -30,15 +40,7 @@ import software.amazon.lambda.powertools.idempotency.model.Product; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.OptionalInt; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -public class BasePersistenceStoreTest { +class BasePersistenceStoreTest { private DataRecord dr; private BasePersistenceStore persistenceStore; @@ -46,7 +48,7 @@ public class BasePersistenceStoreTest { private String validationHash; @BeforeEach - public void setup() { + void setup() { validationHash = null; dr = null; status = -1; @@ -59,14 +61,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; } @@ -78,10 +80,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); @@ -92,13 +92,13 @@ public void saveInProgress_defaultConfig() { assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + 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); @@ -110,14 +110,14 @@ public void saveInProgress_withRemainingTime() { assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + 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") @@ -130,12 +130,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") @@ -151,7 +151,7 @@ public void saveInProgress_jmespath_NotFound_shouldThrowException() { } @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") @@ -164,7 +164,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() @@ -177,8 +177,7 @@ 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())) @@ -187,7 +186,7 @@ public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { } @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() @@ -201,22 +200,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); @@ -229,13 +222,13 @@ public void saveSuccess_shouldUpdateRecord() throws JsonProcessingException { assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); - assertThat(dr.getPayloadHash()).isEqualTo(""); + 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() @@ -247,37 +240,31 @@ public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessi assertThat(status).isEqualTo(2); assertThat(cache).hasSize(1); - DataRecord record = cache.get("testFunction#8d6a8f173b46479eff55e0997864a514"); - 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#8d6a8f173b46479eff55e0997864a514"); - 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#8d6a8f173b46479eff55e0997864a514"); - 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); @@ -285,23 +272,23 @@ public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() .withUseLocalCache(true).build(), "myfunc", cache); Instant now = Instant.now(); - DataRecord dr = new DataRecord( + 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#8d6a8f173b46479eff55e0997864a514", dr); + cache.put("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514", dr1); - DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); - 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); @@ -309,29 +296,29 @@ public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() .withUseLocalCache(true).build(), "myfunc", cache); Instant now = Instant.now(); - DataRecord dr = new DataRecord( + 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#8d6a8f173b46479eff55e0997864a514", dr); + cache.put("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514", dr1); - DataRecord record = persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), now); - assertThat(record.getIdempotencyKey()).isEqualTo("testFunction.myfunc#8d6a8f173b46479eff55e0997864a514"); - 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 @@ -341,14 +328,8 @@ public void getRecord_invalidPayload_shouldThrowValidationException() { .isInstanceOf(IdempotencyValidationException.class); } - //</editor-fold> - // ================================================================= - - // ================================================================= - //<editor-fold desc="deleteRecord"> - @Test - public void deleteRecord_shouldDeleteRecordFromPersistence() { + void deleteRecord_shouldDeleteRecordFromPersistence() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder().build(), null); @@ -357,7 +338,7 @@ 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() @@ -371,11 +352,8 @@ public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { 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")); @@ -383,7 +361,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}) @@ -392,7 +370,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)); @@ -400,16 +378,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/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/dynamodb-local-metadata.json b/powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json deleted file mode 100644 index c76c2c76d..000000000 --- a/powertools-idempotency/powertools-idempotency-dynamodb/dynamodb-local-metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"installationId":"e43b8515-8484-485c-8315-bead4568972b","telemetryEnabled":"true"} \ No newline at end of file diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index ed5b8d5c0..b7c4f317c 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -63,6 +63,29 @@ </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> @@ -71,17 +94,132 @@ <version>2.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> + + <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.0</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> + <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> @@ -94,6 +232,16 @@ </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> 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..f0ba9c4c2 --- /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,325 @@ +[ +{ + "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"] }] +} +] 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/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 index 289b0f1cd..9f6875689 100644 --- 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 @@ -14,16 +14,10 @@ package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; -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 com.amazonaws.services.dynamodbv2.local.main.ServerRunner; -import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; - import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -32,71 +26,40 @@ 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.ResourceInUseException; import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -public class DynamoDBConfig { +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(); - } + 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("http://localhost:" + port)) + .endpointOverride(URI.create(endpoint)) .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(); + 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(); - } - } - - 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); + throw new RuntimeException("Failed to create DynamoDB table", e); } } } diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java index 56b32c4f9..b5c816286 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java @@ -14,10 +14,20 @@ package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +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; @@ -32,47 +42,35 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.Constants; 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; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - /** * 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 @@ -91,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(); @@ -124,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 @@ -150,15 +149,15 @@ 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 should be present due to returnValuesOnConditionCheckFailure("ALL_OLD") - .matches(e -> ((IdempotencyItemAlreadyExistsException) e).getDataRecord().isPresent()); + 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 @@ -170,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 @@ -186,17 +185,15 @@ 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 should be present due to returnValuesOnConditionCheckFailure("ALL_OLD") - .matches(e -> ((IdempotencyItemAlreadyExistsException) e).getDataRecord().isPresent()); - ; + 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 @@ -207,15 +204,8 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire 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 @@ -228,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); @@ -265,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); @@ -299,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()); @@ -336,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"); @@ -368,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); @@ -389,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) @@ -403,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/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java index 7b43542c8..e85614580 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java @@ -16,30 +16,22 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; 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; -public class IdempotencyTest extends DynamoDBConfig { +class IdempotencyTest extends DynamoDBConfig { - @Mock - private Context context; - - @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 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 From 65f00fee20c0182233df2ae79b93b5637b0df4f6 Mon Sep 17 00:00:00 2001 From: kjswaruph <jyothiswaruphkolli@gmail.com> Date: Wed, 27 Aug 2025 22:37:27 +0530 Subject: [PATCH 0821/1008] feat: Support CRaC priming of powertools validation (#2081) * feat: support CRaC priming of powertools validation - Add org.crac dependency and generate-classesloaded-file profile in pom.xml - Add classesloaded.txt in src/main/resources for automatic class preloading - Update ValidationConfig to register for Crac resource - Update spotbugs-exclude.xml to suppress RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT spotbug error in beforeCheckpoint method * test: add ValidationConfigTest to assert beforeCheckpoint and afterRestore hooks do not throw exception * docs: Add SnapStart priming subsection for validation * test: Remove public modifier from ValidationConfigTest class and methods * docs: Improve Lambda SnapStart priming section Co-authored-by: Philipp Page <philipp.page@yahoo.de> --------- Co-authored-by: Philipp Page <philipp.page@yahoo.de> --- docs/utilities/validation.md | 50 + powertools-validation/pom.xml | 25 + .../validation/ValidationConfig.java | 32 +- .../src/main/resources/classesloaded.txt | 7285 +++++++++++++++++ .../validation/ValidationConfigTest.java | 38 + spotbugs-exclude.xml | 5 + 6 files changed, 7434 insertions(+), 1 deletion(-) create mode 100644 powertools-validation/src/main/resources/classesloaded.txt create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationConfigTest.java diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index 96bdd142b..ec35b7034 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -292,3 +292,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/powertools-validation/pom.xml b/powertools-validation/pom.xml index 30e56802f..118edc66f 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -71,6 +71,10 @@ <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-serialization</artifactId> </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> <!-- Test dependencies --> <dependency> @@ -119,4 +123,25 @@ <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> + </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 3f643ab00..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() { } @@ -102,6 +112,26 @@ 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 static final ValidationConfig instance = new ValidationConfig(); } 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/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/spotbugs-exclude.xml b/spotbugs-exclude.xml index 9a42ebf16..35aed5e26 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -191,6 +191,11 @@ </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="MS_SHOULD_BE_FINAL"/> From a7785b30cedfdb2ec8bed6da78d153f5ea52290e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 10:54:05 -0300 Subject: [PATCH 0822/1008] chore: bump actions/dependency-review-action from 4.7.2 to 4.7.3 (#2092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.7.2 to 4.7.3. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/bc41886e18ea39df68b1b1245f4184881938e050...595b5aeba73380359d98a5e087f648dbb0edce1b) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.7.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ana Falcão <anafalcao@poli.ufrj.br> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 3e0ca168c..903b96af9 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -26,6 +26,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents - uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2 + uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3 with: config-file: './.github/dependency-review-config.yml' From 11da7254bff8f61ecd04524c21b11e056f642771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:04:29 -0300 Subject: [PATCH 0823/1008] chore: bump aws.sdk.version from 2.32.30 to 2.32.31 (#2093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps `aws.sdk.version` from 2.32.30 to 2.32.31. Updates `software.amazon.awssdk:bom` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:http-client-spi` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:url-connection-client` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:dynamodb` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:s3` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:lambda` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:kinesis` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:cloudwatch` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:xray` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:sqs` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:cloudformation` from 2.32.30 to 2.32.31 Updates `software.amazon.awssdk:sts` from 2.32.30 to 2.32.31 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.32.31 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.32.31 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ana Falcão <anafalcao@poli.ufrj.br> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index b92f458b0..45dffb792 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.30</aws.sdk.version> + <aws.sdk.version>2.32.31</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index c63e78d7e..7e287d4e5 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.19.2</jackson.version> - <aws.sdk.version>2.32.30</aws.sdk.version> + <aws.sdk.version>2.32.31</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 8cc580958..d7ac21335 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -20,7 +20,7 @@ <maven.shade.version>3.6.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.32.30</aws.sdk.version> + <aws.sdk.version>2.32.31</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> From 58107194c1545115ebc500a198a47c1875ef75a6 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 28 Aug 2025 19:18:05 +0200 Subject: [PATCH 0824/1008] feat(graalvm): GraalVM support for powertools-cloudformation (#2090) --- .github/workflows/check-build.yml | 38 +- GraalVM.md | 26 ++ .../Makefile | 5 + .../infra/sam-graalvm/Dockerfile | 14 + .../infra/sam-graalvm/README.md | 52 +++ .../infra/sam-graalvm/template.yaml | 49 ++ .../pom.xml | 184 +++++--- .../src/main/config/bootstrap | 4 + .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 34 ++ .../resource-config.json | 19 + .../reflect-config.json | 25 + .../helloworld/native-image.properties | 1 + .../helloworld/reflect-config.json | 11 + .../helloworld/resource-config.json | 7 + .../s3/reflect-config.json | 27 ++ powertools-cloudformation/pom.xml | 87 ++++ .../CloudFormationResponse.java | 2 +- .../reflect-config.json | 432 ++++++++++++++++++ .../resource-config.json | 41 ++ .../AbstractCustomResourceHandlerTest.java | 180 ++------ .../CloudFormationIntegrationTest.java | 155 ++----- .../CloudFormationResponseTest.java | 88 ++-- .../ExceptionThrowingHandler.java | 31 ++ .../ExpectedStatusResourceHandler.java | 56 +++ .../ExplicitSuccessResponseHandler.java | 31 ++ .../FailToSendResponseHandler.java | 41 ++ .../cloudformation/FailedResponseHandler.java | 31 ++ .../cloudformation/FailedSendHandler.java | 25 + .../NoOpCustomResourceHandler.java | 34 ++ .../NullCustomResourceHandler.java | 47 ++ .../cloudformation/ResponseTest.java | 8 +- .../SuccessResponseHandler.java | 31 ++ .../cloudformation/SuccessfulSendHandler.java | 25 + .../NoPhysicalResourceIdSetHandler.java | 1 + .../PhysicalResourceIdSetHandler.java | 1 + .../RuntimeExceptionThrownHandler.java | 1 + .../test/resources/simplelogger.properties | 7 + 41 files changed, 1511 insertions(+), 400 deletions(-) create mode 100644 examples/powertools-examples-cloudformation/Makefile create mode 100644 examples/powertools-examples-cloudformation/infra/sam-graalvm/Dockerfile create mode 100644 examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md create mode 100644 examples/powertools-examples-cloudformation/infra/sam-graalvm/template.yaml create mode 100755 examples/powertools-examples-cloudformation/src/main/config/bootstrap create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/native-image.properties create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/reflect-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/helloworld/resource-config.json create mode 100644 examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/software.amazon.awssdk/s3/reflect-config.json create mode 100644 powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/reflect-config.json create mode 100644 powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/resource-config.json create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExceptionThrowingHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExpectedStatusResourceHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ExplicitSuccessResponseHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailToSendResponseHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedResponseHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/FailedSendHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NoOpCustomResourceHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/NullCustomResourceHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessResponseHandler.java create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/SuccessfulSendHandler.java create mode 100644 powertools-cloudformation/src/test/resources/simplelogger.properties diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 3abb1440c..3a34188f9 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -92,6 +92,14 @@ jobs: - id: checkout name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 + with: + files: | + powertools-*/** - name: Setup GraalVM uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1.3.5 with: @@ -100,18 +108,36 @@ jobs: 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 recursively. - # This will make sure to discover new GraalVM supported modules automatically in the future. + # 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") - 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 + 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/GraalVM.md b/GraalVM.md index 56c72d96f..bbddb5e3b 100644 --- a/GraalVM.md +++ b/GraalVM.md @@ -56,5 +56,31 @@ java.lang.InternalError: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defini ``` - 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/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/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..e8deeb8fd --- /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.3.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 45dffb792..5dbb73489 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -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> @@ -73,82 +73,114 @@ <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>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.8.3</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-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-shade-plugin</artifactId> - <version>3.6.0</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> + <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-shade-plugin</artifactId> + <version>3.6.0</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.0</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> </project> 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/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..10152cc64 --- /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,34 @@ +[ + { + "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 + } +] \ 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/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/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 355e8a3ed..862f5832e 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -95,5 +95,92 @@ <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> </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.0</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> + <resources> + <!-- GraalVM Native Image Configuration Files --> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + </build> </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 404137802..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 @@ -46,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; 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..218382888 --- /dev/null +++ b/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/reflect-config.json @@ -0,0 +1,432 @@ +[ +{ + "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"] }] +} +] 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..f3b58337b --- /dev/null +++ b/powertools-cloudformation/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation/resource-config.json @@ -0,0 +1,41 @@ +{ + "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" + }]}, + "bundles":[{ + "name":"jakarta.servlet.LocalStrings", + "locales":[""] + }, { + "name":"jakarta.servlet.http.LocalStrings", + "locales":[""] + }] +} 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 9da18790c..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; 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 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 @@ 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," + @@ -239,7 +241,7 @@ void sendWithNoResponseData() throws Exception { @Test 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 @@ 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 @@ 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(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,15 @@ 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(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," + @@ -328,17 +330,17 @@ void responseBodyStreamFailedResponse() throws Exception { @Test void responseBodyStreamFailedResponseWithReason() throws Exception { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); - Context context = mock(Context.class); + Context context = new TestLambdaContext(); CloudFormationResponse cfnResponse = testableCloudFormationResponse(); String failureReason = "Failed test reason"; - Response failedResponseWithReason = Response.builder(). - status(Response.Status.FAILED).reason(failureReason).build(); + 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\":null," + + "\"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 3e2930541..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,12 +16,14 @@ 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; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; + class ResponseTest { @Test @@ -81,7 +83,7 @@ void explicitReasonWithDefaultValues() { assertThat(response.toString()).contains("Status = SUCCESS"); assertThat(response.toString()).contains("PhysicalResourceId = null"); assertThat(response.toString()).contains("NoEcho = false"); - assertThat(response.toString()).contains("Reason = "+reason); + assertThat(response.toString()).contains("Reason = " + reason); } @Test 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 e55abca03..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; 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/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 From c2d2d51ec8133304830f9353cb94c9bc702ebc39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:21:51 -0300 Subject: [PATCH 0825/1008] chore: bump sam/build-java21 (#2083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps sam/build-java21 from `6ad1664` to `853ac90`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 853ac90b359be42f95fe913fde43517ed5f3ed086aa93a6c0bd4b2d8fe07df9d dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ana Falcão <anafalcao@poli.ufrj.br> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index a4cacd9cb..4ce9ad1ed 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:6ad16645a48cbc9aa6ab9b707350d1976804f410bb47dd0f87bf106cd69ff36a +FROM public.ecr.aws/sam/build-java21@sha256:853ac90b359be42f95fe913fde43517ed5f3ed086aa93a6c0bd4b2d8fe07df9d # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From b1cfa3e6702156a5b748f4bc75bdd01ffdf0a5b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:48:18 -0300 Subject: [PATCH 0826/1008] chore: bump com.github.spotbugs:spotbugs-maven-plugin (#2084) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.3.2 to 4.9.4.0. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.3.2...spotbugs-maven-plugin-4.9.4.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.4.0 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ana Falcão <anafalcao@poli.ufrj.br> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e287d4e5..fffccb89a 100644 --- a/pom.xml +++ b/pom.xml @@ -611,7 +611,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.9.3.2</version> + <version>4.9.4.0</version> <executions> <execution> <id>test</id> From 8998bc9c20c5a7a318771ca91c96aa8087e76f33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 17:59:38 -0300 Subject: [PATCH 0827/1008] chore: bump io.github.ascopes:protobuf-maven-plugin from 3.8.0 to 3.8.1 (#2085) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.8.0 to 3.8.1. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.8.0...v3.8.1) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.8.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 956ca7d8d..85798000f 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.8.0</version> + <version>3.8.1</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 75167ff92..5faa929b7 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -181,7 +181,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.8.0</version> + <version>3.8.1</version> <executions> <execution> <id>generate-test-sources</id> From 19f4ff08bfe1e843acda7d72b32afaa3c38a08a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:10:24 -0300 Subject: [PATCH 0828/1008] chore: bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions (#2088) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions from 0.1.0 to 0.2.0. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions dependency-version: 0.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ana Falcão <anafalcao@poli.ufrj.br> --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index d7ac21335..ffe44eb8e 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -148,7 +148,7 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId> - <version>0.1.0</version> + <version>0.2.0</version> </dependency> </dependencies> </plugin> From b8c5ec7f4b18cd7626fb263690dc8fb8a41695e8 Mon Sep 17 00:00:00 2001 From: Connor Kirkpatrick <17845406+ConnorKirk@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:02:27 +0100 Subject: [PATCH 0829/1008] docs: Update docs introduction --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 17f3b397b..9c5c803cb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ title: Homepage description: Powertools for AWS Lambda (Java) --- -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"} From e502583de3d2c6661494487da575084b39a470b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:03:10 +0200 Subject: [PATCH 0830/1008] chore: bump actions/checkout from 4.2.2 to 5.0.0 (#2087) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.2.2...08c6903cd8c0fde910a37f88322edcfb5dd907a8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 3a34188f9..f254b67d3 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -91,7 +91,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: Get changed files From 62d09392941f9f3f006d6db7156fcd68dc208bb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:03:27 +0200 Subject: [PATCH 0831/1008] chore: bump org.apache.maven.plugins:maven-compiler-plugin (#2094) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.11.0 to 3.14.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.11.0...maven-compiler-plugin-3.14.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ffe44eb8e..3d81e6d1b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <lambda.java.events>3.16.1</lambda.java.events> <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.13.1</aspectj.plugin.version> - <maven.compiler.version>3.11.0</maven.compiler.version> + <maven.compiler.version>3.14.0</maven.compiler.version> <aws.sdk.version>2.32.31</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> From e33f28d74b7b76fcc7f2563dc58858650fdce4b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:07:47 +0200 Subject: [PATCH 0832/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.212.0 to 2.213.0 (#2100) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.212.0 to 2.213.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.212.0...v2.213.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.213.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index bac4f78ff..db4857448 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.3.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.212.0</cdk.version> + <cdk.version>2.213.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.13.4</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 4094bfb20..babfaf7a2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.212.0</cdk.version> + <cdk.version>2.213.0</cdk.version> </properties> <dependencies> From 20bf95285c3932381636bceeaea8f00dd53c6a89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:08:10 +0200 Subject: [PATCH 0833/1008] chore: bump github/codeql-action from 3.29.11 to 3.30.0 (#2106) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.11 to 3.30.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/3c3833e0f8c1c83d449a7478aa59c036a9165498...2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.30.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 4047f0da2..dfa0b2121 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5 + uses: github/codeql-action/upload-sarif@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.29.5 with: sarif_file: results.sarif From 2c3e7a6076c9b1fc8adeb303c301fd876486f336 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:08:35 +0200 Subject: [PATCH 0834/1008] chore: bump aws.sdk.version from 2.32.26 to 2.32.31 (#2098) Bumps `aws.sdk.version` from 2.32.26 to 2.32.31. Updates `software.amazon.awssdk:url-connection-client` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:sdk-core` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:s3` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:kinesis` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:sqs` from 2.32.26 to 2.32.31 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.32.26 to 2.32.31 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.32.31 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.32.31 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.32.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index c456ace5a..9857aa717 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.32.26</sdk.version> + <sdk.version>2.32.31</sdk.version> </properties> <dependencies> From 90387569e51639dc45852053fe16025ba53831e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:21:18 +0200 Subject: [PATCH 0835/1008] chore: bump dev.aspectj:aspectj-maven-plugin from 1.13.1 to 1.14.1 (#2099) Bumps [dev.aspectj:aspectj-maven-plugin](https://github.com/dev-aspectj/aspectj-maven-plugin) from 1.13.1 to 1.14.1. - [Release notes](https://github.com/dev-aspectj/aspectj-maven-plugin/releases) - [Commits](https://github.com/dev-aspectj/aspectj-maven-plugin/compare/aspectj-maven-plugin-1.13.1...aspectj-maven-plugin-1.14.1) --- updated-dependencies: - dependency-name: dev.aspectj:aspectj-maven-plugin dependency-version: 1.14.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 3d81e6d1b..7b8718671 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -18,7 +18,7 @@ <lambda.java.serialization>1.1.6</lambda.java.serialization> <lambda.java.events>3.16.1</lambda.java.events> <maven.shade.version>3.6.0</maven.shade.version> - <aspectj.plugin.version>1.13.1</aspectj.plugin.version> + <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> <aws.sdk.version>2.32.31</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> From de9ba8b32cf344b4536515e583b368ae0bb0423a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 19:00:55 +0200 Subject: [PATCH 0836/1008] chore: bump jackson.version from 2.19.2 to 2.20 (#2097) * chore: bump jackson.version from 2.19.2 to 2.20 Bumps `jackson.version` from 2.19.2 to 2.20. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.19.2 to 2.20 Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.2 to 2.20 Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.19.2 to 2.20 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.19.2 to 2.20 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: '2.20' dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: '2.20' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: '2.20' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: '2.20' dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Migrate to Jackson BOM for managing different Jackson module versions. * Fix issue where root pom file change does not re-trigger graalvm unit tests. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> Co-authored-by: Philipp Page <pagejep@amazon.com> --- .github/workflows/check-build.yml | 1 + pom.xml | 23 +++++------------------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index f254b67d3..b84db997b 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -100,6 +100,7 @@ jobs: with: files: | powertools-*/** + pom.xml - name: Setup GraalVM uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1.3.5 with: diff --git a/pom.xml b/pom.xml index fffccb89a..c1fd3b9c4 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> - <jackson.version>2.19.2</jackson.version> + <jackson.version>2.20.0</jackson.version> <aws.sdk.version>2.32.31</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> @@ -231,24 +231,11 @@ <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> - <version>${jackson.version}</version> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-annotations</artifactId> - <version>${jackson.version}</version> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-jsr310</artifactId> + <groupId>com.fasterxml.jackson</groupId> + <artifactId>jackson-bom</artifactId> <version>${jackson.version}</version> + <scope>import</scope> + <type>pom</type> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> From 22ea06aacd7a0e80310796b90529b4685d141b05 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 4 Sep 2025 10:42:02 +0200 Subject: [PATCH 0837/1008] test(metrics): Improve testability of `@FlushMetrics` annotation (#2112) * Clarify how to mock the Lambda context in unit tests for Metrics utility. * Revert when() mocking in documentation. * Avoid metrics from raising an exception when using plain mocked Lambda context. * Reset outputstream in unit test docs. * Make stdout stream field static and final. * Fix issue when function name is not set. * Fix hl_lines in docs. * Fix hl_lines in docs. * Remove java8 from sam templates. --- docs/core/metrics.md | 64 ++++++--- docs/core/tracing.md | 4 +- powertools-metrics/pom.xml | 19 +++ .../metrics/internal/EmfMetricsLogger.java | 2 +- .../metrics/internal/LambdaMetricsAspect.java | 10 +- .../metrics/RequestHandlerTest.java | 126 ++++++++++++++++++ 6 files changed, 203 insertions(+), 22 deletions(-) create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 31079a76e..61d4c38f0 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -140,7 +140,7 @@ For most use-cases, we recommend using Environment variables and only overwrite Type: AWS::Serverless::Function Properties: ... - Runtime: java8 + Runtime: java11 Environment: Variables: POWERTOOLS_SERVICE_NAME: payment @@ -558,9 +558,24 @@ If you would like to suppress metrics output during your unit tests, you can use 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="23 28 33 50-55" +```java hl_lines="35 40 56-72" import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayOutputStream; @@ -571,6 +586,9 @@ 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; @@ -578,21 +596,25 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import software.amazon.lambda.powertools.metrics.model.MetricUnit; -import software.amazon.lambda.powertools.metrics.testutils.TestContext; +@ExtendWith(MockitoExtension.class) class MetricsTestExample { + @Mock + Context lambdaContext; + private final PrintStream standardOut = System.out; - private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + private ByteArrayOutputStream outputStreamCaptor; private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { + outputStreamCaptor = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStreamCaptor)); } @AfterEach - void tearDown() { + void tearDown() throws Exception { System.setOut(standardOut); } @@ -600,27 +622,37 @@ class MetricsTestExample { void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { // Given RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); Map<String, Object> input = new HashMap<>(); // When - handler.handleRequest(input, context); + handler.handleRequest(input, lambdaContext); // 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"); + 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(namespace = "CustomNamespace", service = "CustomService") + @FlushMetrics(captureColdStart = true) public String handleRequest(Map<String, Object> input, Context context) { Metrics metrics = MetricsFactory.getMetricsInstance(); metrics.addMetric("test-metric", 100, MetricUnit.COUNT); diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 883f8db86..8129d45ba 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -105,7 +105,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: @@ -191,7 +191,7 @@ different supported `captureMode` to record response, exception or both. Type: AWS::Serverless::Function Properties: ... - Runtime: java8 + Runtime: java11 Tracing: Active Environment: diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 4bccab505..b90506c25 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -105,6 +105,11 @@ <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> @@ -135,6 +140,13 @@ </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> @@ -154,6 +166,13 @@ </profile> <profile> <id>graalvm-native</id> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-subclass</artifactId> + <scope>test</scope> + </dependency> + </dependencies> <build> <plugins> <plugin> 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 index 2f6f9e689..c9651585f 100644 --- 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 @@ -204,7 +204,7 @@ public void captureColdStartMetric(Context context, } // Add request ID from context if available - if (context != null) { + if (context != null && context.getAwsRequestId() != null) { coldStartLogger.putProperty(REQUEST_ID_PROPERTY, context.getAwsRequestId()); } 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 b214d7c52..32824e24f 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 @@ -113,7 +113,10 @@ private void captureColdStartMetricIfEnabled(Context extractedContext, FlushMetr } Metrics metricsInstance = MetricsFactory.getMetricsInstance(); - metricsInstance.addMetadata(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); + // This can be null e.g. during unit tests when mocking the Lambda context + if (extractedContext.getAwsRequestId() != null) { + metricsInstance.addMetadata(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); + } // Only capture cold start metrics if enabled on annotation if (metrics.captureColdStart()) { @@ -133,8 +136,9 @@ private void captureColdStartMetricIfEnabled(Context extractedContext, FlushMetr } // Add function name - coldStartDimensions.addDimension("FunctionName", - funcName != null ? funcName : extractedContext.getFunctionName()); + if (funcName != null) { + coldStartDimensions.addDimension("FunctionName", funcName); + } metricsInstance.captureColdStartMetric(extractedContext, coldStartDimensions); } 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..d94a6bbe8 --- /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 SERVICE_NAME + Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); + resetServiceName.setAccessible(true); + resetServiceName.invoke(null); + + // Reset IS_COLD_START + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + 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("metrics"); + 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"; + } + } +} From be3f37fda54a1f897850efd3b1b5eaf4cc59100c Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 4 Sep 2025 13:57:39 +0200 Subject: [PATCH 0838/1008] docs: Add AWS copyright footer. (#2119) --- mkdocs.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 45ea8e64b..ea7617353 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,7 +79,14 @@ 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 From 53b29bdd694f84651775aef7bafb8e2bbde1a232 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 4 Sep 2025 15:50:44 +0200 Subject: [PATCH 0839/1008] feat(logging): Log buffering support for Logj42 and Logback (#2103) * Log buffer PoC with log4j2 appender implementation. * Add buffer cleaning to avoid memory leak and flushBufferOnUncaughtError option to Logging aspect. * Call appenders with logevent directly from BufferingAppender. * Extract buffer management logic into KeyBuffer class and keep log4j appender minimal. * Unit tests for BufferingAppender * Rename to Log4jConstants. * Make Appender detection independent of plugin name. * Add assertions in unit test to test multiple appenders. * Make assertion stronger to assert that each message appears twice because we configured two appenders as proxies. * Add logback implementation. Decouple KeyBuffer and BufferingAppender to avoid circular dependency when flushing buffer on error. * Log error in AppStream example. * Update Logging E2E test to use log buffering. * Prepare Logging E2E test to support paramaterized versions by logging backend. * Add Logging E2E tests for both logback and log4j2. * Debug not found appender on Linux. * Isolate log4j context loading in unit tests. * Avoid logging config leakage in log4j unit test. * Avoid config leaking in logback unit tests. * Update Graal metadata for powertools-logging. * Update Graal metadata for powertools-logging-log4j. * Update Graal metadata for powertools-logging-logback. * Address Sonar findings. * Address pmd findings. * Set allowCommentedBlocks=true in pmd rules. * Fix thread-safety issue in double-checked singleton instance creation. * Increase scope of thread-safety tests in KeyBuffer. * Try to satisfy pmd executor service closing. * Try to satisfy pmd executor service closing. * Add PMD suppressions with reason where appropriate. * Add PMD suppressions with reason where appropriate. * Restore original example. * Enable log sampling again. * Restore formatting in pmd ruleset. * Update Javadoc of BufferingAppenders. * Remove unncessary cleanup. * Remove duplicated infrastructure clearnup in LoggingE2E. * Make javadoc for Keybuffer more concise. * Add comment for large event rejection. * Fix grammer in error message. * Remove redundant tests in PowertoolsLoggingTest. * Add developer documentation. * Fix sonar finding. * Clarify log buffering log level configuration with upper and lower bound example. * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> --------- Co-authored-by: Stefano Vozza <svozza@gmail.com> --- .github/pmd-ruleset.xml | 5 +- docs/core/logging.md | 378 +++++++++++++++- .../sam/src/main/java/helloworld/App.java | 5 - .../src/main/java/helloworld/AppStream.java | 41 +- .../sam/src/main/resources/log4j2.xml | 12 +- .../sam/template.yaml | 4 +- .../{logging => logging-log4j}/pom.xml | 9 +- .../lambda/powertools/e2e/Function.java | 12 +- .../amazon/lambda/powertools/e2e/Input.java | 3 - .../aws-lambda-java-core/reflect-config.json | 0 .../reflect-config.json | 0 .../jni-config.json | 0 .../native-image.properties | 0 .../reflect-config.json | 0 .../resource-config.json | 0 .../reflect-config.json | 0 .../reflect-config.json | 0 .../resource-config.json | 0 .../src/main/resources/log4j2.xml | 11 +- .../handlers/logging-logback/pom.xml | 82 ++++ .../lambda/powertools/e2e/Function.java | 40 ++ .../amazon/lambda/powertools/e2e/Input.java | 38 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 +++ .../resource-config.json | 19 + .../reflect-config.json | 25 ++ .../reflect-config.json | 20 + .../resource-config.json | 7 + .../src/main/resources/logback.xml | 16 + .../amazon/lambda/powertools/e2e/Input.java | 3 - powertools-e2e-tests/handlers/pom.xml | 8 +- .../amazon/lambda/powertools/e2e/Input.java | 3 - powertools-e2e-tests/pom.xml | 5 + .../amazon/lambda/powertools/LoggingE2ET.java | 32 +- .../logging/log4j/BufferingAppender.java | 201 +++++++++ .../internal/Log4jLoggingManager.java | 38 +- .../powertools-logging-log4j/jni-config.json | 16 - .../reflect-config.json | 258 +++-------- .../resource-config.json | 56 --- ...powertools.logging.internal.LoggingManager | 2 +- .../internal/Log4jLoggingManagerTest.java | 51 --- .../internal/handler/PowertoolsArguments.java | 2 +- .../handler/PowertoolsLogEnabled.java | 8 +- .../logging/log4j/BufferingAppenderTest.java | 141 ++++++ .../internal/Log4jLoggingManagerTest.java | 118 +++++ .../resources/log4j2-multiple-buffering.xml | 26 ++ .../src/test/resources/log4j2.xml | 11 +- .../powertools-logging-logback/pom.xml | 5 + .../logging/logback/BufferingAppender.java | 196 +++++++++ .../internal/LogbackLoggingManager.java | 51 ++- .../jni-config.json | 12 - .../reflect-config.json | 85 ++-- .../resource-config.json | 4 - .../logging/LogbackLoggingManagerTest.java | 58 ++- .../logback/BufferingAppenderTest.java | 150 +++++++ .../test/resources/logback-buffering-test.xml | 21 + .../resources/logback-multiple-buffering.xml | 28 ++ .../lambda/powertools/logging/Logging.java | 8 +- .../powertools/logging/PowertoolsLogging.java | 55 +++ .../logging/internal/BufferManager.java | 33 ++ ...anager.java => DefaultLoggingManager.java} | 2 +- .../logging/internal/KeyBuffer.java | 95 ++++ .../logging/internal/LambdaLoggingAspect.java | 186 ++++---- .../internal/LoggingManagerRegistry.java | 99 +++++ .../powertools-logging/jni-config.json | 16 - .../powertools-logging/reflect-config.json | 415 +----------------- .../powertools-logging/resource-config.json | 6 - .../logging/PowertoolsLoggingTest.java | 53 +++ .../handlers/PowertoolsLogErrorNoFlush.java | 15 + .../logging/internal/KeyBufferTest.java | 356 +++++++++++++++ .../internal/LambdaLoggingAspectTest.java | 145 ++++-- .../internal/LoggingManagerRegistryTest.java | 141 ++++++ .../logging/internal/TestLoggingManager.java | 33 +- 76 files changed, 3052 insertions(+), 1043 deletions(-) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/pom.xml (90%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/java/software/amazon/lambda/powertools/e2e/Function.java (86%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/java/software/amazon/lambda/powertools/e2e/Input.java (97%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json (100%) rename powertools-e2e-tests/handlers/{logging => logging-log4j}/src/main/resources/log4j2.xml (52%) create mode 100644 powertools-e2e-tests/handlers/logging-logback/pom.xml create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/logging-logback/src/main/resources/logback.xml create mode 100644 powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppender.java rename powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/{log4 => log4j}/internal/Log4jLoggingManager.java (54%) delete mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppenderTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManagerTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/test/resources/log4j2-multiple-buffering.xml create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/BufferingAppender.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/BufferingAppenderTest.java create mode 100644 powertools-logging/powertools-logging-logback/src/test/resources/logback-buffering-test.xml create mode 100644 powertools-logging/powertools-logging-logback/src/test/resources/logback-multiple-buffering.xml create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogging.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/BufferManager.java rename powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/{DefautlLoggingManager.java => DefaultLoggingManager.java} (94%) create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/KeyBuffer.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistry.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogErrorNoFlush.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/KeyBufferTest.java create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LoggingManagerRegistryTest.java diff --git a/.github/pmd-ruleset.xml b/.github/pmd-ruleset.xml index b93fa19b8..1bc5f3020 100644 --- a/.github/pmd-ruleset.xml +++ b/.github/pmd-ruleset.xml @@ -350,6 +350,9 @@ </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" /> --> @@ -641,4 +644,4 @@ </property> </properties> </rule> -</ruleset> \ No newline at end of file +</ruleset> diff --git a/docs/core/logging.md b/docs/core/logging.md index 2d9e57dda..be3bd7e5c 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -12,6 +12,7 @@ Logging provides an opinionated logger with output structured 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 @@ -311,9 +312,8 @@ We prioritise log level settings in this order: 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. +!!! 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 @@ -787,7 +787,377 @@ with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. } ``` -# Advanced +## Advanced + +### Buffering logs + +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. + +!!! 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." + +=== "log4j2.xml" + + ```xml hl_lines="7-12 16 19" + <?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="DEBUG" + flushOnErrorLog="true"> + <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> + ``` + +=== "logback.xml" + + ```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> + <!-- 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. + +=== "PaymentFunction.java" + + ```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 + } + } + ``` + +#### 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 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 index 2675c96eb..2844e50fe 100644 --- 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 @@ -97,11 +97,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-utilities/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java index 8bc57b201..d3ebbef5d 100644 --- 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 @@ -14,41 +14,45 @@ 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.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.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +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; -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); + 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 + // 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)))) { + PrintWriter writer = new PrintWriter( + new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { - log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + log.info( + "Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); } catch (IOException e) { @@ -56,4 +60,3 @@ public void handleRequest(InputStream input, OutputStream output, Context contex } } } - 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 index e1fd14cea..e140022e4 100644 --- a/examples/powertools-examples-core-utilities/sam/src/main/resources/log4j2.xml +++ b/examples/powertools-examples-core-utilities/sam/src/main/resources/log4j2.xml @@ -4,13 +4,13 @@ <Console name="JsonAppender" target="SYSTEM_OUT"> <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </Console> + <BufferingAppender name="BufferedAppender" bufferAtVerbosity="DEBUG"> + <AppenderRef ref="JsonAppender" /> + </BufferingAppender> </Appenders> <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> - </Logger> - <Root level="info"> - <AppenderRef ref="JsonAppender"/> + <Root level="debug"> + <AppenderRef ref="BufferedAppender" /> </Root> </Loggers> -</Configuration> \ No newline at end of file +</Configuration> diff --git a/examples/powertools-examples-core-utilities/sam/template.yaml b/examples/powertools-examples-core-utilities/sam/template.yaml index 9a51a1ba9..6b1814dce 100644 --- a/examples/powertools-examples-core-utilities/sam/template.yaml +++ b/examples/powertools-examples-core-utilities/sam/template.yaml @@ -1,4 +1,4 @@ -AWSTemplateFormatVersion: '2010-09-09' +AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: > CoreUtilities @@ -13,7 +13,7 @@ Globals: Environment: Variables: # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables - POWERTOOLS_LOG_LEVEL: INFO + 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 diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml similarity index 90% rename from powertools-e2e-tests/handlers/logging/pom.xml rename to powertools-e2e-tests/handlers/logging-log4j/pom.xml index f8b5689c8..d415ae4b3 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -8,20 +8,15 @@ <version>2.3.0</version> </parent> - <artifactId>e2e-test-handler-logging</artifactId> + <artifactId>e2e-test-handler-logging-log4j</artifactId> <packaging>jar</packaging> - <name>E2E test handler – Logging</name> + <name>E2E test handler – Logging Log4j</name> <dependencies> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging-log4j</artifactId> </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-layout-template-json</artifactId> - <version>2.25.1</version> - </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> 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 86% 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 58492653a..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,12 +14,15 @@ package software.amazon.lambda.powertools.e2e; -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 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); @@ -29,6 +32,9 @@ 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"; } -} \ No newline at end of file +} diff --git a/powertools-e2e-tests/handlers/logging/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 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-log4j/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-log4j/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/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json diff --git a/powertools-e2e-tests/handlers/logging/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 similarity index 100% rename from powertools-e2e-tests/handlers/logging/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json diff --git a/powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/log4j2.xml similarity index 52% rename from powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml rename to powertools-e2e-tests/handlers/logging-log4j/src/main/resources/log4j2.xml index 8925f70b9..28e03a9e0 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/resources/log4j2.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/log4j2.xml @@ -4,13 +4,14 @@ <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="JsonAppender"/> + <AppenderRef ref="BufferedAppender" /> </Root> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> - </Logger> </Loggers> -</Configuration> \ No newline at end of file +</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..932eb0612 --- /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.3.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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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/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 1328ded77..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 @@ -23,9 +23,6 @@ public class Input { private String highResolution; - public Input() { - } - public Map<String, Double> getMetrics() { return metrics; } diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7b8718671..53f9ed3ad 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -28,7 +28,8 @@ <module>batch</module> <module>largemessage</module> <module>largemessage_idempotent</module> - <module>logging</module> + <module>logging-log4j</module> + <module>logging-logback</module> <module>tracing</module> <module>metrics</module> <module>idempotency</module> @@ -56,6 +57,11 @@ <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-tracing</artifactId> 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/pom.xml b/powertools-e2e-tests/pom.xml index babfaf7a2..5f3e2506a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -102,6 +102,11 @@ <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> 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 ad2c2564f..f5d2cea84 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 @@ -25,10 +25,11 @@ 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; @@ -37,20 +38,19 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; +@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 = 10, unit = TimeUnit.MINUTES) - static void setup() { + private void setupInfrastructure(String pathToFunction) { infrastructure = Infrastructure.builder() - .testName(LoggingE2ET.class.getSimpleName()) + .testName(LoggingE2ET.class.getSimpleName() + "-" + pathToFunction) .tracing(true) - .pathToFunction("logging") + .pathToFunction(pathToFunction) .environmentVariables( Stream.of(new String[][] { { "POWERTOOLS_LOG_LEVEL", "INFO" }, @@ -62,15 +62,19 @@ static void setup() { functionName = outputs.get(FUNCTION_NAME_OUTPUT); } - @AfterAll - static void tearDown() { + @AfterEach + void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } - @Test - void test_logInfoWithAdditionalKeys() throws JsonProcessingException { + @ParameterizedTest + @ValueSource(strings = { "logging-log4j", "logging-logback" }) + @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 + "\"}}"; 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/log4/internal/Log4jLoggingManager.java b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManager.java similarity index 54% rename from powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/Log4jLoggingManager.java rename to powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManager.java index 4e57a8e45..90bbe1d32 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/Log4jLoggingManager.java +++ b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jLoggingManager.java @@ -12,19 +12,26 @@ * */ -package software.amazon.lambda.powertools.logging.log4.internal; +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 (see {@link LoggingManager}). + * LoggingManager for Log4j2 that provides log level management and buffer operations. + * Implements both {@link LoggingManager} and {@link BufferManager} interfaces. */ -public class Log4jLoggingManager implements LoggingManager { +public class Log4jLoggingManager implements LoggingManager, BufferManager { /** * @inheritDoc @@ -45,4 +52,29 @@ 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/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 index 2c4de0562..c8b081385 100644 --- 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 @@ -3,22 +3,6 @@ "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-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 index 9b2afe183..adbd9e0c1 100644 --- 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 @@ -1,4 +1,10 @@ [ +{ + "name":"[Ljava.lang.Object;" +}, +{ + "name":"[Ljava.lang.String;" +}, { "name":"[Lorg.apache.logging.log4j.core.Appender;" }, @@ -15,16 +21,7 @@ "name":"[Lorg.apache.logging.log4j.layout.template.json.JsonTemplateLayout$EventTemplateAdditionalField;" }, { - "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.services.lambda.runtime.RequestHandler", - "allDeclaredClasses":true, - "queryAllPublicMethods":true + "name":"com.amazonaws.services.lambda.runtime.Context" }, { "name":"com.amazonaws.services.lambda.runtime.events.SQSEvent$MessageAttribute", @@ -60,155 +57,88 @@ "name":"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", "methods":[{"name":"<init>","parameterTypes":[] }] }, -{ - "name":"com.sun.tools.attach.VirtualMachine" -}, { "name":"jakarta.servlet.Servlet" }, { "name":"java.io.Serializable", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":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":[] }] + "queryAllDeclaredMethods":true }, { "name":"java.lang.Cloneable", "queryAllDeclaredMethods":true }, { - "name":"java.lang.Comparable", - "allDeclaredClasses":true, - "queryAllPublicMethods":true -}, -{ - "name":"java.lang.Enum", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":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.Iterable", + "queryAllDeclaredMethods":true }, { "name":"java.lang.Object", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] + "allDeclaredFields":true }, { "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.String" }, -{ - "name":"java.lang.System", - "methods":[{"name":"getSecurityManager","parameterTypes":[] }] -}, { "name":"java.lang.Thread", "fields":[{"name":"threadLocalRandomProbe"}] }, { - "name":"java.lang.annotation.Retention", - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true -}, -{ - "name":"java.lang.annotation.Target", - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true -}, -{ - "name":"java.lang.constant.Constable", - "allDeclaredClasses":true, - "queryAllPublicMethods":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.sql.Date" }, { - "name":"java.lang.reflect.AccessibleObject", - "methods":[{"name":"setAccessible","parameterTypes":["boolean"] }] + "name":"java.sql.Time" }, { - "name":"java.lang.reflect.AnnotatedArrayType", - "methods":[{"name":"getAnnotatedGenericComponentType","parameterTypes":[] }] + "name":"java.util.AbstractCollection", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.reflect.AnnotatedType", - "methods":[{"name":"getType","parameterTypes":[] }] + "name":"java.util.AbstractList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.reflect.Executable", - "methods":[{"name":"getAnnotatedExceptionTypes","parameterTypes":[] }, {"name":"getAnnotatedParameterTypes","parameterTypes":[] }, {"name":"getAnnotatedReceiverType","parameterTypes":[] }, {"name":"getParameterCount","parameterTypes":[] }, {"name":"getParameters","parameterTypes":[] }] + "name":"java.util.AbstractMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.reflect.Method", - "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] + "name":"java.util.Arrays$ArrayList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true }, { - "name":"java.lang.reflect.Parameter", - "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] + "name":"java.util.Collection", + "queryAllDeclaredMethods":true }, { - "name":"java.security.AccessController", - "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true }, { - "name":"java.sql.Date" + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] }, { - "name":"java.sql.Time" + "name":"java.util.List", + "queryAllDeclaredMethods":true }, { - "name":"java.util.Collections$UnmodifiableMap", - "fields":[{"name":"m"}] + "name":"java.util.Map", + "queryAllDeclaredMethods":true }, { - "name":"java.util.concurrent.ForkJoinTask", - "fields":[{"name":"aux"}, {"name":"status"}] + "name":"java.util.RandomAccess", + "queryAllDeclaredMethods":true }, { "name":"java.util.concurrent.atomic.AtomicBoolean", @@ -230,10 +160,7 @@ "name":"javax.servlet.Servlet" }, { - "name":"jdk.internal.misc.Unsafe" -}, -{ - "name":"kotlin.jvm.JvmInline" + "name":"kotlin.Metadata" }, { "name":"org.apache.logging.log4j.core.appender.AbstractAppender$Builder", @@ -493,6 +420,12 @@ "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" }, @@ -743,7 +676,13 @@ "name":"org.apache.logging.log4j.core.layout.MessageLayout" }, { - "name":"org.apache.logging.log4j.core.layout.PatternLayout" + "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" @@ -878,9 +817,7 @@ "name":"org.apache.logging.log4j.core.pattern.ClassNamePatternConverter" }, { - "name":"org.apache.logging.log4j.core.pattern.DatePatternConverter", - "queryAllDeclaredMethods":true, - "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] + "name":"org.apache.logging.log4j.core.pattern.DatePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.EncodingPatternConverter" @@ -913,9 +850,7 @@ "name":"org.apache.logging.log4j.core.pattern.IntegerPatternConverter" }, { - "name":"org.apache.logging.log4j.core.pattern.LevelPatternConverter", - "queryAllDeclaredMethods":true, - "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] + "name":"org.apache.logging.log4j.core.pattern.LevelPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.LineLocationPatternConverter" @@ -929,9 +864,7 @@ "name":"org.apache.logging.log4j.core.pattern.LoggerFqcnPatternConverter" }, { - "name":"org.apache.logging.log4j.core.pattern.LoggerPatternConverter", - "queryAllDeclaredMethods":true, - "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] + "name":"org.apache.logging.log4j.core.pattern.LoggerPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.MapPatternConverter" @@ -990,9 +923,7 @@ "name":"org.apache.logging.log4j.core.pattern.ThreadIdPatternConverter" }, { - "name":"org.apache.logging.log4j.core.pattern.ThreadNamePatternConverter", - "queryAllDeclaredMethods":true, - "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] + "name":"org.apache.logging.log4j.core.pattern.ThreadNamePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.ThreadPriorityPatternConverter" @@ -1105,29 +1036,6 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"getInstance","parameterTypes":[] }] }, -{ - "name":"org.apache.logging.log4j.layout.template.json.resolver.PowerToolsResolverFactoryTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogInEcsFormat","parameterTypes":[] }, {"name":"shouldLogInJsonFormat","parameterTypes":[] }] -}, -{ - "name":"org.apache.logging.log4j.layout.template.json.resolver.PowertoolsResolverArgumentsTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingKeyValue","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingRawJson","parameterTypes":[] }] -}, -{ - "name":"org.apache.logging.log4j.layout.template.json.resolver.PowertoolsResolverFactory", - "queryAllDeclaredMethods":true, - "methods":[{"name":"getInstance","parameterTypes":[] }] -}, { "name":"org.apache.logging.log4j.layout.template.json.resolver.SourceResolverFactory", "queryAllDeclaredMethods":true, @@ -1161,12 +1069,6 @@ "name":"org.apiguardian.api.API", "queryAllPublicMethods":true }, -{ - "name":"org.aspectj.runtime.internal.AroundClosure", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, { "name":"org.jctools.queues.MpmcArrayQueue" }, @@ -1178,48 +1080,8 @@ "fields":[{"name":"IS_COLD_START"}] }, { - "name":"software.amazon.lambda.powertools.logging.internal.Log4jLoggingManagerTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getLogLevel_shouldReturnConfiguredLogLevel","parameterTypes":[] }, {"name":"resetLogLevel","parameterTypes":[] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments", - "allDeclaredClasses":true, + "name":"software.amazon.lambda.powertools.logging.log4j.BufferingAppender", "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments$ArgumentFormat", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"sun.reflect.ReflectionFactory", - "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] + "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"] }] } ] 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 index aca0e0356..cf017fdeb 100644 --- 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 @@ -12,8 +12,6 @@ "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" }, { "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" - }, { - "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" }, { "pattern":"\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E" }, { @@ -36,60 +34,6 @@ "pattern":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" }, { "pattern":"\\QStackTraceElementLayout.json\\E" - }, { - "pattern":"\\Qlog4j2-test.jsn\\E" - }, { - "pattern":"\\Qlog4j2-test.json\\E" - }, { - "pattern":"\\Qlog4j2-test.properties\\E" - }, { - "pattern":"\\Qlog4j2-test.xml\\E" - }, { - "pattern":"\\Qlog4j2-test.yaml\\E" - }, { - "pattern":"\\Qlog4j2-test.yml\\E" - }, { - "pattern":"\\Qlog4j2-test18b4aac2.jsn\\E" - }, { - "pattern":"\\Qlog4j2-test18b4aac2.json\\E" - }, { - "pattern":"\\Qlog4j2-test18b4aac2.properties\\E" - }, { - "pattern":"\\Qlog4j2-test18b4aac2.xml\\E" - }, { - "pattern":"\\Qlog4j2-test18b4aac2.yaml\\E" - }, { - "pattern":"\\Qlog4j2-test18b4aac2.yml\\E" - }, { - "pattern":"\\Qlog4j2.StatusLogger.properties\\E" - }, { - "pattern":"\\Qlog4j2.component.properties\\E" - }, { - "pattern":"\\Qlog4j2.jsn\\E" - }, { - "pattern":"\\Qlog4j2.json\\E" - }, { - "pattern":"\\Qlog4j2.properties\\E" - }, { - "pattern":"\\Qlog4j2.system.properties\\E" - }, { - "pattern":"\\Qlog4j2.xml\\E" - }, { - "pattern":"\\Qlog4j2.yaml\\E" - }, { - "pattern":"\\Qlog4j2.yml\\E" - }, { - "pattern":"\\Qlog4j218b4aac2.jsn\\E" - }, { - "pattern":"\\Qlog4j218b4aac2.json\\E" - }, { - "pattern":"\\Qlog4j218b4aac2.properties\\E" - }, { - "pattern":"\\Qlog4j218b4aac2.xml\\E" - }, { - "pattern":"\\Qlog4j218b4aac2.yaml\\E" - }, { - "pattern":"\\Qlog4j218b4aac2.yml\\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 index d444c5525..d4b2a72a0 100644 --- 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 @@ -1 +1 @@ -software.amazon.lambda.powertools.logging.log4.internal.Log4jLoggingManager \ No newline at end of file +software.amazon.lambda.powertools.logging.log4j.internal.Log4jLoggingManager diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java deleted file mode 100644 index 69e1ee710..000000000 --- a/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/internal/Log4jLoggingManagerTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package software.amazon.lambda.powertools.logging.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.slf4j.event.Level.DEBUG; -import static org.slf4j.event.Level.ERROR; -import static org.slf4j.event.Level.WARN; - -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; -import software.amazon.lambda.powertools.logging.log4.internal.Log4jLoggingManager; - -class Log4jLoggingManagerTest { - - private final static Logger LOG = LoggerFactory.getLogger(Log4jLoggingManagerTest.class); - private final static Logger ROOT = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - - @Test - @Order(1) - 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 - @Order(2) - 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); - } -} 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 index 1fc235ff7..0d95f29fa 100644 --- 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 @@ -34,7 +34,7 @@ import software.amazon.lambda.powertools.utilities.JsonConfig; public class PowertoolsArguments implements RequestHandler<SQSEvent.SQSMessage, String> { - private final Logger LOG = LoggerFactory.getLogger(PowertoolsArguments.class); + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsArguments.class); private final ArgumentFormat argumentFormat; public PowertoolsArguments(ArgumentFormat argumentFormat) { 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 index e8c0c5851..0ee7f14fa 100644 --- 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 @@ -14,15 +14,17 @@ 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 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 final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); + private static final Logger LOG = LoggerFactory.getLogger(PowertoolsLogEnabled.class); @Override @Logging(clearState = true) 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..434d3983b --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/BufferingAppenderTest.java @@ -0,0 +1,141 @@ +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 { + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } + + @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/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 index 778077bc5..870e3a803 100644 --- a/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml +++ b/powertools-logging/powertools-logging-log4j/src/test/resources/log4j2.xml @@ -10,8 +10,17 @@ <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>q + <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"/> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 433a3774a..e2c16019f 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -85,6 +85,11 @@ <artifactId>jsonassert</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit-pioneer</groupId> + <artifactId>junit-pioneer</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/internal/LogbackLoggingManager.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackLoggingManager.java index 86982b444..906ebdad5 100644 --- 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 @@ -14,18 +14,29 @@ package software.amazon.lambda.powertools.logging.logback.internal; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; +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 (see {@link LoggingManager}). + * LoggingManager for Logback that provides log level management and buffer operations. + * Implements both {@link LoggingManager} and {@link BufferManager} interfaces. */ -public class LogbackLoggingManager implements LoggingManager { +public class LogbackLoggingManager implements LoggingManager, BufferManager { private final LoggerContext loggerContext; @@ -56,4 +67,34 @@ public void setLogLevel(org.slf4j.event.Level logLevel) { 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/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 index 753dafdea..c8b081385 100644 --- 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 @@ -3,18 +3,6 @@ "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.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 index 683933a77..dfc50427f 100644 --- 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 @@ -1,4 +1,15 @@ [ +{ + "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":[] }] @@ -20,6 +31,18 @@ "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" }, @@ -69,6 +92,10 @@ { "name":"java.lang.Object" }, +{ + "name":"java.lang.ProcessEnvironment", + "fields":[{"name":"theCaseInsensitiveEnvironment"}, {"name":"theEnvironment"}] +}, { "name":"java.lang.String" }, @@ -103,6 +130,10 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, +{ + "name":"java.util.Collections$UnmodifiableMap", + "fields":[{"name":"m"}] +}, { "name":"java.util.List", "queryAllDeclaredMethods":true @@ -135,59 +166,9 @@ "fields":[{"name":"IS_COLD_START"}] }, { - "name":"software.amazon.lambda.powertools.logging.LogbackLoggingManagerTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"getLogLevel_shouldReturnConfiguredLogLevel","parameterTypes":[] }, {"name":"resetLogLevel","parameterTypes":[] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.LambdaEcsEncoderTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogException","parameterTypes":[] }, {"name":"shouldLogInEcsFormat","parameterTypes":[] }, {"name":"shouldNotLogCloudInfo","parameterTypes":[] }, {"name":"shouldNotLogFunctionInfo","parameterTypes":[] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.LambdaJsonEncoderTest", - "allDeclaredFields":true, - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, + "name":"software.amazon.lambda.powertools.logging.logback.BufferingAppender", "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cleanUp","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingKeyValue","parameterTypes":[] }, {"name":"shouldLogArgumentsAsJsonWhenUsingRawJson","parameterTypes":[] }, {"name":"shouldLogEventAsStringForStreamHandler","parameterTypes":[] }, {"name":"shouldLogEventForHandlerWhenEnvVariableSetToTrue","parameterTypes":[] }, {"name":"shouldLogEventForHandlerWithLogEventAnnotation","parameterTypes":[] }, {"name":"shouldLogException","parameterTypes":[] }, {"name":"shouldLogInJsonFormat","parameterTypes":[] }, {"name":"shouldLogResponseForHandlerWhenEnvVariableSetToTrue","parameterTypes":[] }, {"name":"shouldLogResponseForHandlerWithLogResponseAnnotation","parameterTypes":[] }, {"name":"shouldLogResponseForStreamHandler","parameterTypes":[] }, {"name":"shouldLogStructuredArgumentsAsNewEntries","parameterTypes":[] }, {"name":"shouldLogThreadInfo","parameterTypes":[] }, {"name":"shouldLogTimestampDifferently","parameterTypes":[] }, {"name":"shouldNotLogEventForHandlerWhenEnvVariableSetToFalse","parameterTypes":[] }, {"name":"shouldNotLogPowertoolsInfo","parameterTypes":[] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsArguments", - "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.SQSEvent$SQSMessage","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEvent", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEventDisabled", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEventForStream", - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogResponse", - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogResponseForStream", - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] + "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", 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 index 2fc3c56bd..33d1d61c4 100644 --- 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 @@ -4,16 +4,12 @@ "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" }, { "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" - }, { - "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\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":"\\Qlogback.scmo\\E" }]}, "bundles":[] } 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 index 214057917..60f158739 100644 --- 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 @@ -15,15 +15,27 @@ 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 org.junit.jupiter.api.Order; +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 { @@ -31,8 +43,18 @@ 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 - @Order(1) void getLogLevel_shouldReturnConfiguredLogLevel() { LogbackLoggingManager manager = new LogbackLoggingManager(); Level logLevel = manager.getLogLevel(LOG); @@ -43,7 +65,6 @@ void getLogLevel_shouldReturnConfiguredLogLevel() { } @Test - @Order(2) void resetLogLevel() { LogbackLoggingManager manager = new LogbackLoggingManager(); manager.setLogLevel(ERROR); @@ -51,4 +72,35 @@ void resetLogLevel() { 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/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/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/src/main/java/software/amazon/lambda/powertools/logging/Logging.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java index 9e5e735d1..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 @@ -72,7 +72,7 @@ /** * 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_RESPONE' environment variable + * Can also be configured with the 'POWERTOOLS_LOGGER_LOG_RESPONSE' environment variable */ boolean logResponse() default false; @@ -102,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/PowertoolsLogging.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogging.java new file mode 100644 index 000000000..1276c2a87 --- /dev/null +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogging.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.logging; + +import software.amazon.lambda.powertools.logging.internal.BufferManager; +import software.amazon.lambda.powertools.logging.internal.LoggingManager; +import software.amazon.lambda.powertools.logging.internal.LoggingManagerRegistry; + +/** + * PowertoolsLogging provides a backend-independent API for log buffering operations. + * This class abstracts away the underlying logging framework (Log4j2, Logback) and + * provides a unified interface for buffer management. + */ +public final class PowertoolsLogging { + + private PowertoolsLogging() { + // Utility class + } + + /** + * 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(); + } + } +} 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/DefautlLoggingManager.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLoggingManager.java similarity index 94% rename from powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefautlLoggingManager.java rename to powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLoggingManager.java index 5326f53e6..ed2c14c38 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefautlLoggingManager.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLoggingManager.java @@ -21,7 +21,7 @@ * 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 DefautlLoggingManager implements LoggingManager { +public class DefaultLoggingManager implements LoggingManager { @Override public void setLogLevel(Level logLevel) { 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/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java index eccfdae4f..591283996 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 @@ -35,9 +35,6 @@ import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_TRACE_ID; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SERVICE; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.JsonNode; -import io.burt.jmespath.Expression; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -45,14 +42,10 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.PrintStream; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import java.util.Locale; import java.util.Random; -import java.util.ServiceLoader; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -63,10 +56,14 @@ import org.slf4j.MDC; import org.slf4j.MarkerFactory; 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.Logging; import software.amazon.lambda.powertools.utilities.JsonConfig; - @Aspect @DeclarePrecedence("*, software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect") public final class LambdaLoggingAspect { @@ -77,7 +74,7 @@ public final class LambdaLoggingAspect { private static final LoggingManager LOGGING_MANAGER; static { - LOGGING_MANAGER = getLoggingManagerFromServiceLoader(); + LOGGING_MANAGER = LoggingManagerRegistry.getLoggingManager(); setLogLevel(); @@ -90,7 +87,8 @@ static void setLogLevel() { 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.", + 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); } } @@ -102,7 +100,7 @@ static void setLogLevel() { private static Level getLevelFromString(String level) { if (Arrays.stream(Level.values()).anyMatch(slf4jLevel -> slf4jLevel.name().equalsIgnoreCase(level))) { - return Level.valueOf(level.toUpperCase()); + return Level.valueOf(level.toUpperCase(Locale.ROOT)); } else { // FATAL does not exist in slf4j if ("FATAL".equalsIgnoreCase(level)) { @@ -113,60 +111,14 @@ private static Level getLevelFromString(String level) { return Level.INFO; } - /** - * Use {@link ServiceLoader} to lookup for a {@link LoggingManager}. - * A file <i>software.amazon.lambda.powertools.logging.internal.LoggingManager</i> must be created in - * <i>META-INF/services/</i> folder with the appropriate implementation of the {@link LoggingManager} - * - * @return an instance of {@link LoggingManager} - * @throws IllegalStateException if no {@link LoggingManager} could be found - */ - @SuppressWarnings("java:S106") // S106: System.err is used rather than logger to make sure message is printed - private static LoggingManager getLoggingManagerFromServiceLoader() { - 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 getLoggingManager(loggingManagerList, System.err); - } - - static LoggingManager getLoggingManager(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 DefautlLoggingManager(); - } 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 to your dependencies"); - printStream.println("WARN. Using the first LoggingManager found on the classpath: [" + loggingManagerList.get(0) + "]"); - } - loggingManager = loggingManagerList.get(0); - } - return loggingManager; - } - private static void setLogLevels(Level logLevel) { LOGGING_MANAGER.setLogLevel(logLevel); } - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(logging)") public void callAt(Logging logging) { + // Pointcut method - body intentionally empty } /** @@ -174,69 +126,51 @@ public void callAt(Logging logging) { */ @Around(value = "callAt(logging) && execution(@Logging * *.*(..))", argNames = "pjp,logging") public Object around(ProceedingJoinPoint pjp, - Logging logging) throws Throwable { + Logging logging) throws Throwable { boolean isOnRequestHandler = placedOnRequestHandler(pjp); boolean isOnRequestStreamHandler = placedOnStreamHandler(pjp); setLogLevelBasedOnSamplingRate(pjp, logging); - addLambdaContextToLoggingContext(pjp); - getXrayTraceId().ifPresent(xRayTraceId -> MDC.put(FUNCTION_TRACE_ID.getName(), xRayTraceId)); - // Log Event Object[] proceedArgs = logEvent(pjp, logging, isOnRequestHandler, isOnRequestStreamHandler); if (!logging.correlationIdPath().isEmpty()) { - captureCorrelationId(logging.correlationIdPath(), proceedArgs, isOnRequestHandler, isOnRequestStreamHandler); + captureCorrelationId(logging.correlationIdPath(), proceedArgs, isOnRequestHandler, + 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 + @SuppressWarnings("PMD.CloseResource") // Lambda-owned stream, not ours to close OutputStream backupOutputStream = null; - if ((logging.logResponse() || POWERTOOLS_LOG_RESPONSE) && isOnRequestStreamHandler) { - backupOutputStream = (OutputStream) proceedArgs[1]; - proceedArgs[1] = new ByteArrayOutputStream(); + 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); } Object lambdaFunctionResponse; - try { - // Call Function Handler lambdaFunctionResponse = pjp.proceed(proceedArgs); - } catch (Throwable t) { - if (logging.logError() || POWERTOOLS_LOG_ERROR) { - // logging the exception with additional context - logger(pjp).error(MarkerFactory.getMarker("FATAL"), "Exception in Lambda Handler", t); - } + } catch (Throwable t) { // NOPMD - AspectJ proceed() throws Throwable + handleException(pjp, logging, t); throw t; } finally { - if (logging.clearState()) { - MDC.clear(); - } - coldStartDone(); + performCleanup(logging); } - // Log Response - 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); - } - } + logResponse(pjp, logging, lambdaFunctionResponse, isOnRequestHandler, isOnRequestStreamHandler, + backupOutputStream, proceedArgs); return lambdaFunctionResponse; } private Object[] logEvent(ProceedingJoinPoint pjp, Logging logging, - boolean isOnRequestHandler, boolean isOnRequestStreamHandler) { + boolean isOnRequestHandler, boolean isOnRequestStreamHandler) { Object[] proceedArgs = pjp.getArgs(); if (logging.logEvent() || POWERTOOLS_LOG_EVENT) { @@ -260,7 +194,7 @@ private void addLambdaContextToLoggingContext(ProceedingJoinPoint pjp) { } private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, - final Logging logging) { + final Logging logging) { double samplingRate = samplingRate(logging); if (isHandlerMethod(pjp)) { @@ -346,9 +280,9 @@ private void logRequestStreamHandlerResponse(final ProceedingJoinPoint pjp, fina } private void captureCorrelationId(final String correlationIdPath, - Object[] proceedArgs, - final boolean isOnRequestHandler, - final boolean isOnRequestStreamHandler) { + Object[] proceedArgs, + final boolean isOnRequestHandler, + final boolean isOnRequestStreamHandler) { if (isOnRequestHandler) { JsonNode jsonNode = JsonConfig.get().getObjectMapper().valueToTree(proceedArgs[0]); setCorrelationIdFromNode(correlationIdPath, jsonNode); @@ -377,11 +311,10 @@ private void setCorrelationIdFromNode(String correlationIdPath, JsonNode jsonNod } } - 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); + 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))) { @@ -392,6 +325,53 @@ private byte[] bytesFromInputStreamSafely(final InputStream inputStream) throws } } + 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 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); + } + } + + private void performCleanup(Logging logging) { + if (logging.clearState()) { + MDC.clear(); + } + if (LOGGING_MANAGER instanceof BufferManager) { + ((BufferManager) LOGGING_MANAGER).clearBuffer(); + } + coldStartDone(); + } + + 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 Logger logger(final ProceedingJoinPoint pjp) { return LoggerFactory.getLogger(pjp.getSignature().getDeclaringType()); } 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/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 index 2c4de0562..c8b081385 100644 --- 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 @@ -3,22 +3,6 @@ "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-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 index 7347b8400..4c66ebd97 100644 --- 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 @@ -23,21 +23,7 @@ "methods":[{"name":"<init>","parameterTypes":[] }] }, { - "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.services.lambda.runtime.RequestHandler", - "allDeclaredClasses":true, - "queryAllPublicMethods":true -}, -{ - "name":"com.amazonaws.services.lambda.runtime.RequestStreamHandler", - "allDeclaredClasses":true, - "queryAllPublicMethods":true + "name":"com.amazonaws.services.lambda.runtime.Context" }, { "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", @@ -129,6 +115,7 @@ "name":"com.amazonaws.services.lambda.runtime.tests.EventArgumentsProvider", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, "methods":[{"name":"<init>","parameterTypes":[] }] }, { @@ -139,9 +126,6 @@ "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", "methods":[{"name":"<init>","parameterTypes":[] }] }, -{ - "name":"com.sun.tools.attach.VirtualMachine" -}, { "name":"double", "queryAllDeclaredMethods":true @@ -159,137 +143,43 @@ "name":"java.io.Serializable", "allDeclaredClasses":true, "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "queryAllDeclaredConstructors":true + "queryAllPublicMethods":true }, { "name":"java.lang.Boolean" }, -{ - "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.Cloneable", "queryAllDeclaredMethods":true }, { - "name":"java.lang.Comparable", - "allDeclaredClasses":true, - "queryAllPublicMethods":true -}, -{ - "name":"java.lang.Enum", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":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, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"getClass","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] + "name":"java.lang.Object" }, { "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.String" }, { - "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.util.AbstractMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true }, { - "name":"java.lang.annotation.Target", + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, -{ - "name":"java.lang.constant.Constable", - "allDeclaredClasses":true, - "queryAllPublicMethods":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.Map" -}, -{ - "name":"java.util.concurrent.ForkJoinTask", - "fields":[{"name":"aux"}, {"name":"status"}] + "name":"java.util.Map", + "queryAllDeclaredMethods":true }, { "name":"java.util.concurrent.atomic.AtomicBoolean", @@ -303,22 +193,10 @@ "name":"java.util.function.Consumer", "queryAllPublicMethods":true }, -{ - "name":"jdk.internal.misc.Unsafe" -}, -{ - "name":"kotlin.jvm.JvmInline" -}, { "name":"org.apiguardian.api.API", "queryAllPublicMethods":true }, -{ - "name":"org.aspectj.runtime.internal.AroundClosure", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, { "name":"org.joda.time.DateTime" }, @@ -349,42 +227,6 @@ "allDeclaredClasses":true, "queryAllPublicMethods":true }, -{ - "name":"org.slf4j.test.OutputChoice", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"org.slf4j.test.OutputChoice$OutputChoiceType", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"org.slf4j.test.TestLogger", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"org.slf4j.test.TestLoggerConfiguration", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"org.slf4j.test.TestLoggerFactory", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"org.slf4j.test.TestServiceProvider", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", "fields":[{"name":"IS_COLD_START"}, {"name":"SERVICE_NAME"}] @@ -396,232 +238,11 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"arrayArgument","parameterTypes":[] }, {"name":"jsonArgument","parameterTypes":[] }, {"name":"keyValueArgument","parameterTypes":[] }, {"name":"mapArgument","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayHttpApiCorrelationId$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogApiGatewayRestApiCorrelationId$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAppSyncCorrelationId", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAppSyncCorrelationId$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogClearState", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.util.Map","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogClearState$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogDisabled", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogDisabledForStream", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"anotherMethod","parameterTypes":[] }, {"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabled$AjcClosure3", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledForStream", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledForStream$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogError", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogError$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEvent", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEvent$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] + "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.handlers.PowertoolsLogEventBridgeCorrelationId$AjcClosure1", + "name":"software.amazon.lambda.powertools.logging.internal.BufferManager", "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventEnvVar", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventEnvVar$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventForStream", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventForStream$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponse", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponse$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponseForStream", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.io.InputStream","java.io.OutputStream","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogResponseForStream$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingDisabled", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingDisabled$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingEnabled", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, - "methods":[{"name":"handleRequest","parameterTypes":["java.lang.Object","com.amazonaws.services.lambda.runtime.Context"] }] -}, -{ - "name":"software.amazon.lambda.powertools.logging.handlers.PowertoolsLogSamplingEnabled$AjcClosure1", - "allDeclaredClasses":true, - "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { @@ -647,23 +268,15 @@ { "name":"software.amazon.lambda.powertools.logging.model.Basket", "allDeclaredFields":true, - "allDeclaredClasses":true, "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getProducts","parameterTypes":[] }] }, { "name":"software.amazon.lambda.powertools.logging.model.Product", "allDeclaredFields":true, - "allDeclaredClasses":true, "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getId","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPrice","parameterTypes":[] }] -}, -{ - "name":"sun.reflect.ReflectionFactory", - "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] } ] 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 index ca77675e0..832be3d72 100644 --- 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 @@ -2,8 +2,6 @@ "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" }, { @@ -14,10 +12,6 @@ "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" }, { "pattern":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" - }, { - "pattern":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/Europe/Berlin\\E" - }, { - "pattern":"\\Qcom/amazonaws/lambda/thirdparty/org/joda/time/tz/data/ZoneInfoMap\\E" }]}, "bundles":[] } 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..ea3a2f3f6 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.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 software.amazon.lambda.powertools.logging; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import software.amazon.lambda.powertools.logging.internal.LoggingManagerRegistry; +import software.amazon.lambda.powertools.logging.internal.TestLoggingManager; + +class PowertoolsLoggingTest { + + private TestLoggingManager testManager; + + @BeforeEach + void setUp() { + // Get the TestLoggingManager instance from registry + testManager = (TestLoggingManager) LoggingManagerRegistry.getLoggingManager(); + testManager.resetBufferState(); + } + + @Test + void testFlushBuffer_shouldCallBufferManager() { + // WHEN + PowertoolsLogging.flushBuffer(); + + // THEN + assertThat(testManager.isBufferFlushed()).isTrue(); + } + + @Test + void testClearBuffer_shouldCallBufferManager() { + // WHEN + PowertoolsLogging.clearBuffer(); + + // THEN + assertThat(testManager.isBufferCleared()).isTrue(); + } +} 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/internal/KeyBufferTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/KeyBufferTest.java new file mode 100644 index 000000000..15a54fa5c --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/KeyBufferTest.java @@ -0,0 +1,356 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.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 (IOException e) { + // may not be there in the first run + } + } + + @AfterEach + void cleanUp() throws IOException { + // Make sure file is cleaned up after each test + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + } + + @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 751d195b5..3ff531321 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 @@ -31,8 +31,6 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.channels.FileChannel; @@ -40,7 +38,6 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -81,6 +78,7 @@ 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; @@ -111,6 +109,13 @@ void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTar 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) { @@ -721,47 +726,129 @@ void shouldLogCorrelationIdOnAppSyncEvent() throws IOException { } @Test - void testMultipleLoggingManagers_shouldWarnAndSelectFirstOne() throws UnsupportedEncodingException { - // GIVEN - List<LoggingManager> list = new ArrayList<>(); - list.add(new TestLoggingManager()); - list.add(new DefautlLoggingManager()); + void shouldClearBufferAfterSuccessfulHandlerExecution() { + // WHEN + requestHandler.handleRequest(new Object(), context); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PrintStream stream = new PrintStream(outputStream); + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + } + } + @Test + void shouldClearBufferAfterSuccessfulStreamHandlerExecution() throws IOException { // WHEN - LambdaLoggingAspect.getLoggingManager(list, stream); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); // 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 to your dependencies") - .contains("WARN. Using the first LoggingManager found on the classpath: [" + list.get(0) + "]"); + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferCleared()).isTrue(); + } + } + + @Test + void shouldClearBufferAfterHandlerExceptionWithLogError() { + // 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).isBufferCleared()).isTrue(); + } + } + + @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; + } } @Test - void testNoLoggingManagers_shouldWarnAndCreateDefault() throws UnsupportedEncodingException { + void shouldFlushBufferOnUncaughtErrorWhenEnabled() { // GIVEN - List<LoggingManager> list = new ArrayList<>(); + requestHandler = new PowertoolsLogError(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PrintStream stream = new PrintStream(outputStream); + // WHEN + try { + requestHandler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } + + // THEN + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferFlushed()).isTrue(); + } + } + + @Test + void shouldNotFlushBufferOnUncaughtErrorWhenDisabled() { + // GIVEN + PowertoolsLogErrorNoFlush handler = new PowertoolsLogErrorNoFlush(); // WHEN - LoggingManager loggingManager = LambdaLoggingAspect.getLoggingManager(list, stream); + try { + handler.handleRequest("input", context); + } catch (Exception e) { + // ignore + } // 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"); + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + if (loggingManager instanceof TestLoggingManager) { + assertThat(((TestLoggingManager) loggingManager).isBufferFlushed()).isFalse(); + } + } - assertThat(loggingManager).isExactlyInstanceOf(DefautlLoggingManager.class); + @Test + void shouldClearBufferBeforeErrorLoggingWhenFlushBufferOnUncaughtErrorDisabled() { + // GIVEN + PowertoolsLogErrorNoFlush handler = new PowertoolsLogErrorNoFlush(); + + // WHEN + try { + 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(); + } } private void resetLogLevel(Level level) 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 index 0958e0d3b..f2aa2417e 100644 --- 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 @@ -7,17 +7,19 @@ import org.slf4j.test.TestLogger; import org.slf4j.test.TestLoggerFactory; -public class TestLoggingManager implements LoggingManager { +public class TestLoggingManager implements LoggingManager, BufferManager { private final TestLoggerFactory loggerFactory; + private boolean bufferFlushed = false; + private boolean bufferCleared = false; public TestLoggingManager() { - ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); - if (!(loggerFactory instanceof TestLoggerFactory)) { + ILoggerFactory loggerFactoryInstance = LoggerFactory.getILoggerFactory(); + if (!(loggerFactoryInstance instanceof TestLoggerFactory)) { throw new RuntimeException( "LoggerFactory does not match required type: " + TestLoggerFactory.class.getName()); } - this.loggerFactory = (TestLoggerFactory) loggerFactory; + this.loggerFactory = (TestLoggerFactory) loggerFactoryInstance; } @Override @@ -29,4 +31,27 @@ public void setLogLevel(Level logLevel) { 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; + } } From ea0aed1827f6df3caa39181a9346a6c9d850892b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:08:11 +0200 Subject: [PATCH 0840/1008] chore: bump squidfunk/mkdocs-material in /docs (#2127) Bumps squidfunk/mkdocs-material from `1a4e939` to `209b62d`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 209b62dd9530163cc5cf9a49853b5bb8570ffb3f3b5fe4eadc1d319bbda5ce2f dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 4d3135506..6fef5966e 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:1a4e939cfd62b90943b6a829eb8544933e4e94eab18b8ca1d0f912fa9a532c37 +FROM squidfunk/mkdocs-material@sha256:209b62dd9530163cc5cf9a49853b5bb8570ffb3f3b5fe4eadc1d319bbda5ce2f COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 5c2f0311228aed84f81342231972d9c2e21ac7a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:08:40 +0200 Subject: [PATCH 0841/1008] chore: bump github/codeql-action from 3.30.0 to 3.30.1 (#2126) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.0 to 3.30.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d...f1f6e5f6af878fb37288ce1c627459e94dbf7d01) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.30.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index dfa0b2121..8ff72b365 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d # v3.29.5 + uses: github/codeql-action/upload-sarif@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.29.5 with: sarif_file: results.sarif From fdbe3a25434b161e2620d9ab670cd2f7fcddf6bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:08:51 +0200 Subject: [PATCH 0842/1008] chore: bump com.github.spotbugs:spotbugs-maven-plugin (#2125) Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.4.0 to 4.9.4.2. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.4.0...spotbugs-maven-plugin-4.9.4.2) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1fd3b9c4..f96c434a8 100644 --- a/pom.xml +++ b/pom.xml @@ -598,7 +598,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.9.4.0</version> + <version>4.9.4.2</version> <executions> <execution> <id>test</id> From cb9e4d4aaddf25720cdad586b97ac456ed136c25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:09:08 +0200 Subject: [PATCH 0843/1008] chore: bump aws-actions/configure-aws-credentials from 4.3.1 to 5.0.0 (#2120) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 4.3.1 to 5.0.0. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/7474bc4690e29a8392af63c5b98e7449536d5c3a...a03048d87541d1d9fcf2ecf528a4a65ba9bd7838) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index bf241cc1b..a9d3fbf7b 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -41,7 +41,7 @@ jobs: 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@7474bc4690e29a8392af63c5b98e7449536d5c3a + uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 462bfbc34..f18964852 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -64,7 +64,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 + uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 @@ -97,7 +97,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 + uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08dfa90dc..61dd6234d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -281,7 +281,7 @@ jobs: 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@7474bc4690e29a8392af63c5b98e7449536d5c3a + uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} From eb8bc4e8edaf7941ea470201ace8e0aedb711f58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:09:24 +0200 Subject: [PATCH 0844/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.213.0 to 2.214.0 (#2117) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.213.0 to 2.214.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.213.0...v2.214.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.214.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index db4857448..daf50203c 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.3.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.213.0</cdk.version> + <cdk.version>2.214.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.13.4</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 5f3e2506a..1bf9d0e75 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.213.0</cdk.version> + <cdk.version>2.214.0</cdk.version> </properties> <dependencies> From 7d7906615d28a0b36a01d8f95c03d518b1712e79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:09:43 +0200 Subject: [PATCH 0845/1008] chore: bump graalvm/setup-graalvm from 1.3.5 to 1.3.6 (#2116) Bumps [graalvm/setup-graalvm](https://github.com/graalvm/setup-graalvm) from 1.3.5 to 1.3.6. - [Release notes](https://github.com/graalvm/setup-graalvm/releases) - [Commits](https://github.com/graalvm/setup-graalvm/compare/7f488cf82a3629ee755e4e97342c01d6bed318fa...7a1da54cb7fdef4ea19f6ecdfa9ecf59dc5a48fe) --- updated-dependencies: - dependency-name: graalvm/setup-graalvm dependency-version: 1.3.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index b84db997b..482ba4186 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -102,7 +102,7 @@ jobs: powertools-*/** pom.xml - name: Setup GraalVM - uses: graalvm/setup-graalvm@7f488cf82a3629ee755e4e97342c01d6bed318fa # v1.3.5 + uses: graalvm/setup-graalvm@7a1da54cb7fdef4ea19f6ecdfa9ecf59dc5a48fe # v1.3.6 with: java-version: "21" distribution: "graalvm" From 1ffc374d2dd62360d1fdb3a4f141621893cd73d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:09:55 +0200 Subject: [PATCH 0846/1008] chore: bump aws.sdk.version from 2.32.31 to 2.33.1 (#2115) Bumps `aws.sdk.version` from 2.32.31 to 2.33.1. Updates `software.amazon.awssdk:bom` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:http-client-spi` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:dynamodb` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:lambda` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:cloudwatch` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:xray` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:cloudformation` from 2.32.31 to 2.33.1 Updates `software.amazon.awssdk:sts` from 2.32.31 to 2.33.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.33.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.33.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.33.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 5dbb73489..f27a16181 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.32.31</aws.sdk.version> + <aws.sdk.version>2.33.2</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index f96c434a8..6a6b97844 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.32.31</aws.sdk.version> + <aws.sdk.version>2.33.2</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 53f9ed3ad..ca5aaa8af 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -20,7 +20,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.32.31</aws.sdk.version> + <aws.sdk.version>2.33.2</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> From 7c5acf508ad8e3e1fabafbc3c74706b61f22f159 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:10:13 +0200 Subject: [PATCH 0847/1008] chore: bump io.github.ascopes:protobuf-maven-plugin from 3.8.1 to 3.9.0 (#2114) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.8.1 to 3.9.0. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.8.1...v3.9.0) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 85798000f..e3f1dd153 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.8.1</version> + <version>3.9.0</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 5faa929b7..6f24c1aac 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -181,7 +181,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.8.1</version> + <version>3.9.0</version> <executions> <execution> <id>generate-test-sources</id> From b36d8bac90087d5ccda1c87eac338ef50edd4ff2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:10:27 +0200 Subject: [PATCH 0848/1008] chore: bump org.yaml:snakeyaml from 2.4 to 2.5 (#2111) Bumps [org.yaml:snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 2.4 to 2.5. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.5..snakeyaml-2.4) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-version: '2.5' dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1bf9d0e75..ef9836226 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -152,7 +152,7 @@ <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> - <version>2.4</version> + <version>2.5</version> <scope>test</scope> </dependency> <dependency> From a2eecae9a9f69047efa43500031f19bcb96c5b14 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 9 Sep 2025 12:18:49 +0200 Subject: [PATCH 0849/1008] chore(ci): Fix circular dependency in dynamodb-local and maven packaging phases. (#2129) --- powertools-idempotency/powertools-idempotency-dynamodb/pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index b7c4f317c..64b7b49b3 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -186,6 +186,9 @@ <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> From 4c1520eee38d59628b38433afeada0a74230731b Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 9 Sep 2025 13:07:18 +0200 Subject: [PATCH 0850/1008] Skip maven deployment for e2e test handlers. (#2131) --- powertools-e2e-tests/handlers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ca5aaa8af..aa172d704 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -13,7 +13,6 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <lambda.java.core>1.3.0</lambda.java.core> <lambda.java.serialization>1.1.6</lambda.java.serialization> <lambda.java.events>3.16.1</lambda.java.events> @@ -22,6 +21,7 @@ <maven.compiler.version>3.14.0</maven.compiler.version> <aws.sdk.version>2.33.2</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> + <maven.deploy.skip>true</maven.deploy.skip> </properties> <modules> From 29e52c2fded7042560d4eaa0f87ad4048b203701 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 9 Sep 2025 13:36:03 +0200 Subject: [PATCH 0851/1008] chore(ci): Do not use Mockito SNAPSHOT version for release. (#2137) --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 6a6b97844..9b91ab2c9 100644 --- a/pom.xml +++ b/pom.xml @@ -536,6 +536,12 @@ <profiles> <profile> <id>release</id> + <!-- TODO: Revert once 5.19.1 is stable released. --> + <!-- https://github.com/aws-powertools/powertools-lambda-java/issues/2079 --> + <properties> + <mockito.version>5.19.0</mockito.version> + <mockito-junit-jupiter.version>5.19.0</mockito-junit-jupiter.version> + </properties> <build> <plugins> <plugin> @@ -637,6 +643,7 @@ </plugins> </build> </profile> + </profiles> </project> From 3de08f958b22e14ec6e5469b31d6e213de0b13b9 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 9 Sep 2025 14:30:38 +0200 Subject: [PATCH 0852/1008] chore(ci): Set mockito SNAPSHOT version only for Graal profiles. (#2138) --- pom.xml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 9b91ab2c9..86f7858cb 100644 --- a/pom.xml +++ b/pom.xml @@ -114,8 +114,8 @@ <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.18.0</versions-maven-plugin.version> <elastic.version>1.7.0</elastic.version> - <mockito.version>5.19.1-SNAPSHOT</mockito.version> - <mockito-junit-jupiter.version>5.19.1-SNAPSHOT</mockito-junit-jupiter.version> + <mockito.version>5.19.0</mockito.version> + <mockito-junit-jupiter.version>5.19.0</mockito-junit-jupiter.version> <junit-pioneer.version>2.3.0</junit-pioneer.version> <crac.version>1.5.0</crac.version> @@ -643,7 +643,21 @@ </plugins> </build> </profile> - + <profile> + <id>generate-graalvm-files</id> + <properties> + <mockito.version>5.19.1-SNAPSHOT</mockito.version> + <mockito-junit-jupiter.version>5.19.1-SNAPSHOT</mockito-junit-jupiter.version> + </properties> + </profile> + <profile> + <id>graalvm-native</id> + <properties> + <mockito.version>5.19.1-SNAPSHOT</mockito.version> + <mockito-junit-jupiter.version>5.19.1-SNAPSHOT</mockito-junit-jupiter.version> + </properties> + </profile> + </profiles> </project> From 9200c9caf69cc099a7054a9d586bfc5f6fc92957 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:17:02 +0200 Subject: [PATCH 0853/1008] chore(ci): bump version to 2.4.0 (#2139) Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 58 files changed, 65 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 62d34e9c5..5e0266744 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 554e97168..b892f43ce 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 9857aa717..aa42acaf6 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index b2951b28b..262a6b14f 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.3.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.4.0718 ``` ### Deploy with CDK @@ -32,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-2.3.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.4.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index e8deeb8fd..0f06fe084 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.3.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.4.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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index f27a16181..1de9921a3 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index e02a636bf..e1d0e7c1d 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index daf50203c..491c9ec68 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.214.0</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 8770d5b31..1325eadaf 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.3.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.3.0' - aspect 'software.amazon.lambda:powertools-metrics:2.3.0' + aspect 'software.amazon.lambda:powertools-tracing:2.4.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.4.0' + aspect 'software.amazon.lambda:powertools-metrics:2.4.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index ca0604517..98c41093f 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.3.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.3.0") - aspect("software.amazon.lambda:powertools-metrics:2.3.0") + aspect("software.amazon.lambda:powertools-tracing:2.4.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.4.0") + aspect("software.amazon.lambda:powertools-metrics:2.4.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 302fa8074..668e24cf8 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 3df44f441..ae85d9efd 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index c4dc9c099..3f1559b12 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index f02189948..cc83aa5f6 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 6abb743f9..47ca3e50c 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-idempotency-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM</name> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index a5f49d9c6..32b595e48 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index e3f1dd153..149310c60 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 815506325..a03b5d819 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index f10a20aab..9d83adb0b 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index e660aeba2..e99400d0f 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 64ee8c465..0c8e7c89b 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index bdd14e5fb..7e6f48445 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.3.0</version> + <version>2.4.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index ea7617353..372dfedd7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -128,7 +128,7 @@ extra_javascript: extra: powertools: - version: 2.3.0 + version: 2.4.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 86f7858cb..8e79eea34 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index bbdc5b0c2..b389df6fe 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 862f5832e..ca4e7536f 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index ea9baa98c..7620d84ee 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 630ff7cfa..99ce7d3c4 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index b9555ee47..79fd5b8e0 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 3aa876bfc..3aa6c5bee 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index dea0f7263..4261ff459 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index d415ae4b3..792e19fba 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-logging-log4j</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index 932eb0612..ac643b969 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-logging-logback</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 809f7faa2..54f9015c5 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index d1cd4bf74..957242f0f 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index aa172d704..d26cbf740 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 304941b0c..0a88cc92c 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index c595b7201..dcecfc0a8 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index bdcc6813b..f4e9ff341 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index ef9836226..a0d500686 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index e20d90565..096c6ea07 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 2f575d3de..b2a6459a5 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 64b7b49b3..191d75eeb 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 6f24c1aac..1ffa533fc 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index d61dacf48..b78fa225b 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 7a288870a..14cc8898d 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 836f8c53f..92283b0cf 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index e2c16019f..ff0312001 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index b90506c25..c1ac723f1 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 617e0cc00..f7347c03a 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index ed92930fc..bbb6a1111 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index b8857b0c9..fa969e585 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index b486a1bb3..55c254f88 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 709ea6634..d1dc88d84 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 6dcf2c4ae..136b2ef43 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.3.0</version> + <version>2.4.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 7e4e2af15..2cc8fa5d4 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 67de0be7d..6081abd66 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 118edc66f..1c048e66a 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.3.0</version> + <version>2.4.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From f8a02854b64656cfb81ce27554494aba7ca32727 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:55:52 +0200 Subject: [PATCH 0854/1008] chore: bump aws.sdk.version from 2.33.2 to 2.33.5 (#2132) Bumps `aws.sdk.version` from 2.33.2 to 2.33.5. Updates `software.amazon.awssdk:bom` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:http-client-spi` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.33.5 Updates `software.amazon.awssdk:dynamodb` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.33.5 Updates `software.amazon.awssdk:lambda` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.33.5 Updates `software.amazon.awssdk:cloudwatch` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:xray` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.33.5 Updates `software.amazon.awssdk:cloudformation` from 2.33.2 to 2.33.5 Updates `software.amazon.awssdk:sts` from 2.33.2 to 2.33.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.33.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.33.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.33.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 1de9921a3..0abe6e5d6 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <maven.compiler.target>11</maven.compiler.target> <lambda.core.version>1.3.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> - <aws.sdk.version>2.33.2</aws.sdk.version> + <aws.sdk.version>2.33.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 8e79eea34..ddf703375 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.33.2</aws.sdk.version> + <aws.sdk.version>2.33.5</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index d26cbf740..fca2fec7b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.33.2</aws.sdk.version> + <aws.sdk.version>2.33.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From a34689d0bef26285b4d3bdfaabe57523800f2c1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:56:19 +0200 Subject: [PATCH 0855/1008] chore: bump org.apache.kafka:kafka-clients from 4.0.0 to 4.1.0 (#2134) Bumps org.apache.kafka:kafka-clients from 4.0.0 to 4.1.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-version: 4.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 149310c60..7f9449361 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -24,7 +24,7 @@ <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> - <version>4.0.0</version> <!-- Supports >= 3.0.0 --> + <version>4.1.0</version> <!-- Supports >= 3.0.0 --> </dependency> <dependency> <groupId>org.apache.avro</groupId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 1ffa533fc..b0705a204 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -34,7 +34,7 @@ </description> <properties> - <kafka-clients.version>4.0.0</kafka-clients.version> + <kafka-clients.version>4.1.0</kafka-clients.version> <avro.version>1.12.0</avro.version> <protobuf.version>4.32.0</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> From d14e73e4c43ac0b89f983f38c1f18877045ddc01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:57:07 +0200 Subject: [PATCH 0856/1008] chore: bump com.amazonaws:aws-lambda-java-core from 1.3.0 to 1.4.0 (#2135) Bumps [com.amazonaws:aws-lambda-java-core](https://github.com/aws/aws-lambda-java-libs) from 1.3.0 to 1.4.0. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-core dependency-version: 1.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index aa42acaf6..c31277d6a 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 0abe6e5d6..eb762665b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -12,7 +12,7 @@ <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <lambda.core.version>1.3.0</lambda.core.version> + <lambda.core.version>1.4.0</lambda.core.version> <lambda.events.version>3.16.1</lambda.events.version> <aws.sdk.version>2.33.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index e1d0e7c1d..db3750e53 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 668e24cf8..a47be2458 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -34,7 +34,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index ae85d9efd..3647509c6 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 3f1559b12..b5236b11e 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index cc83aa5f6..95fcc07c1 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 47ca3e50c..7b34c94c2 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index 32b595e48..8bc2a6311 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -48,7 +48,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index a03b5d819..3bd5a5de7 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -34,7 +34,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 9d83adb0b..09a67104f 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -33,7 +33,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index e99400d0f..a85e466fe 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -26,7 +26,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 0c8e7c89b..2bdaafbd1 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -26,7 +26,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 7e6f48445..303ce73cb 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -41,7 +41,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> - <version>1.3.0</version> + <version>1.4.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> diff --git a/pom.xml b/pom.xml index ddf703375..b2e225659 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ <aws.xray.recorder.version>2.19.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.3.0</lambda.core.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.0</maven-compiler-plugin.version> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index fca2fec7b..9bc7c5e37 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -13,7 +13,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <lambda.java.core>1.3.0</lambda.java.core> + <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.0</maven.shade.version> From a2fdf23a2183276b6fe300320178afd883322bac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:57:21 +0200 Subject: [PATCH 0857/1008] chore: bump sam/build-java21 (#2141) Bumps sam/build-java21 from `853ac90` to `fd3b445`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: fd3b44561b51626c15ae4437630afcfdc84ad883edca64d2cdb87aad755eb7ac dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 4ce9ad1ed..5086f4532 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:853ac90b359be42f95fe913fde43517ed5f3ed086aa93a6c0bd4b2d8fe07df9d +FROM public.ecr.aws/sam/build-java21@sha256:fd3b44561b51626c15ae4437630afcfdc84ad883edca64d2cdb87aad755eb7ac # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 87d691b5122fa314a4211041480517da5cf85e11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:57:40 +0200 Subject: [PATCH 0858/1008] chore: bump tj-actions/changed-files from 46.0.5 to 47.0.0 (#2143) Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 46.0.5 to 47.0.0. - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/ed68ef82c095e0d48ec87eccea555d944a631a4c...24d32ffd492484c1d75e0c0b894501ddb9d30d62) --- updated-dependencies: - dependency-name: tj-actions/changed-files dependency-version: 47.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 482ba4186..09a232188 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -96,7 +96,7 @@ jobs: fetch-depth: 0 - name: Get changed files id: changed-files - uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 + uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0 with: files: | powertools-*/** From 08e7c948f6a633e9f27d9f8df1d0944c6ecb9b02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:57:51 +0200 Subject: [PATCH 0859/1008] chore: bump squidfunk/mkdocs-material in /docs (#2144) Bumps squidfunk/mkdocs-material from `209b62d` to `86d21da`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 86d21da4f45f16e30774bf911e5b4795da13ce0cd197dbf8d3d059f256b2cc37 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 6fef5966e..696d43a12 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:209b62dd9530163cc5cf9a49853b5bb8570ffb3f3b5fe4eadc1d319bbda5ce2f +FROM squidfunk/mkdocs-material@sha256:86d21da4f45f16e30774bf911e5b4795da13ce0cd197dbf8d3d059f256b2cc37 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 4d9fc6023836a93bb621b45821759bf654462215 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:58:52 +0200 Subject: [PATCH 0860/1008] chore: bump org.codehaus.mojo:versions-maven-plugin (#2148) Bumps [org.codehaus.mojo:versions-maven-plugin](https://github.com/mojohaus/versions) from 2.18.0 to 2.19.1. - [Release notes](https://github.com/mojohaus/versions/releases) - [Changelog](https://github.com/mojohaus/versions/blob/master/ReleaseNotes.md) - [Commits](https://github.com/mojohaus/versions/compare/2.18.0...2.19.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:versions-maven-plugin dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b2e225659..cb14997c4 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ <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.18.0</versions-maven-plugin.version> + <versions-maven-plugin.version>2.19.1</versions-maven-plugin.version> <elastic.version>1.7.0</elastic.version> <mockito.version>5.19.0</mockito.version> <mockito-junit-jupiter.version>5.19.0</mockito-junit-jupiter.version> From e2707c5b628f34d1c039a9008e9eecad449dd99d Mon Sep 17 00:00:00 2001 From: Kyle Zurawski <39493372+kylez-ithaka@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:41:56 -0400 Subject: [PATCH 0861/1008] docs(logger): Fix logging environment variables names in documentation (#2161) The table of environment variables had the wrong variable names, even though other references in the same file had the correct values --- docs/core/logging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index be3bd7e5c..db01a3ec0 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -209,9 +209,9 @@ There are some other environment variables which can be set to modify Logging's | 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_LOG_EVENT` | boolean | Specify if the incoming Lambda event should be logged. See [Logging event](#logging-incoming-event) | -| `POWERTOOLS_LOG_RESPONSE` | boolean | Specify if the Lambda response should be logged. See [logging response](#logging-handler-response) | -| `POWERTOOLS_LOG_ERROR` | boolean | Specify if a Lambda uncaught exception should be logged. See [logging exception](#logging-handler-uncaught-exception ) | +| `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 From fe5aef6360a5551bc7aa9d19501c8cf24976b157 Mon Sep 17 00:00:00 2001 From: Ahmed Kamel <humanzz@hotmail.com> Date: Thu, 2 Oct 2025 11:46:20 +0100 Subject: [PATCH 0862/1008] feat(metrics): introduce Metrics.flushMetrics (#2154) * feat(metrics): introduce Metrics.flushMetrics - introduce `Metrics.flushMetrics` as a more powerful version of `flushSingleMetrics` to allow - using defaults by inheriting state e.g. namespace, dimensions and metadata - emitting multiple metrics in one metrics context - refactor `flushSingleMetrics` to use `flushMetrics` - move namespace/service setting from `MetricsFactory` to `EmfMetricsLogger` * feat(metrics): introduce Metrics.flushMetrics - introduce `Metrics.flushMetrics` as a more powerful version of `flushSingleMetrics` to allow - using defaults by inheriting state e.g. namespace, dimensions and metadata - emitting multiple metrics in one metrics context - refactor `flushSingleMetrics` to use `flushMetrics` - move namespace/service setting from `MetricsFactory` to `EmfMetricsLogger` * address metrics context issue * fix addDimension wrongly adding to defaultDimensions * introduce Metrics.addProperty and use it instead of addMetadata * use flushMetrics in captureColdStartMetric * use putProperty for addMetadata and consolidate duplicate logic * update docs and javadoc * Update docs/core/metrics.md Co-authored-by: Philipp Page <philipp.page@yahoo.de> --------- Co-authored-by: Philipp Page <philipp.page@yahoo.de> --- docs/core/metrics.md | 30 +++--- .../lambda/powertools/metrics/Metrics.java | 27 ++++- .../metrics/internal/EmfMetricsLogger.java | 99 +++++-------------- .../metrics/internal/LambdaMetricsAspect.java | 33 +++---- .../metrics/internal/MetricsUtils.java | 22 +++++ .../internal/EmfMetricsLoggerTest.java | 41 +++++++- .../metrics/testutils/TestMetrics.java | 6 ++ 7 files changed, 140 insertions(+), 118 deletions(-) create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUtils.java diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 61d4c38f0..71c56bb8b 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -462,9 +462,9 @@ If you wish to set custom default dimensions, it can be done via `#!java metrics 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 --> -### Creating a single metric with different configuration +### Creating metrics with different configuration -You can create a single metric with its own namespace and dimensions using `flushSingleMetric`: +You can create metrics with different configurations e.g. different namespace and/or dimensions using `flushMetrics()`: === "App.java" @@ -480,13 +480,17 @@ You can create a single metric with its own namespace and dimensions using `flus @Override @FlushMetrics(namespace = "ServerlessAirline", service = "payment") public Object handleRequest(Object input, Context context) { - metrics.flushSingleMetric( - "CustomMetric", - 1, - MetricUnit.COUNT, - "CustomNamespace", - DimensionSet.of("CustomDimension", "value") // Dimensions are optional - ); + 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")); + }); } } ``` @@ -516,7 +520,7 @@ The following example shows how to configure a custom `Metrics` Singleton using public class App implements RequestHandler<Object, Object> { // Create and configure a Metrics singleton without annotation - private static final Metrics customMetrics = MetricsBuilder.builder() + private static final Metrics metrics = MetricsBuilder.builder() .withNamespace("ServerlessAirline") .withRaiseOnEmptyMetrics(true) .withService("payment") @@ -527,11 +531,11 @@ The following example shows how to configure a custom `Metrics` Singleton using // You can manually capture the cold start metric // Lambda context is an optional argument if not available in your environment // Dimensions are also optional. - customMetrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); + metrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); // Add metrics to the custom metrics singleton - customMetrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); - customMetrics.flush(); + metrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + metrics.flush(); } } ``` 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 d21fe163e..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 @@ -16,6 +16,7 @@ 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; @@ -162,7 +163,15 @@ default void captureColdStartMetric() { } /** - * Flush a single metric with custom dimensions. This creates a separate metrics context + * 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); + + /** + * 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 @@ -171,10 +180,17 @@ default void captureColdStartMetric() { * @param namespace the namespace for the metric * @param dimensions custom dimensions for this metric (optional) */ - void flushSingleMetric(String name, double value, MetricUnit unit, String namespace, DimensionSet dimensions); + 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); + }); + + } /** - * Flush a single metric with custom dimensions. This creates a separate metrics context + * 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 @@ -183,6 +199,9 @@ default void captureColdStartMetric() { * @param namespace the namespace for the metric */ default void flushSingleMetric(String name, double value, MetricUnit unit, String namespace) { - flushSingleMetric(name, value, unit, namespace, null); + flushMetrics(metrics -> { + metrics.setNamespace(namespace); + metrics.addMetric(name, value, unit); + }); } } 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 index c9651585f..37f2d193a 100644 --- 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 @@ -14,7 +14,6 @@ package software.amazon.lambda.powertools.metrics.internal; -import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.getXrayTraceId; import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.isColdStart; import java.time.Instant; @@ -22,6 +21,7 @@ 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; @@ -43,16 +43,15 @@ */ public class EmfMetricsLogger implements Metrics { private static final Logger LOGGER = LoggerFactory.getLogger(EmfMetricsLogger.class); - private static final String TRACE_ID_PROPERTY = "xray_trace_id"; - private static final String REQUEST_ID_PROPERTY = "function_request_id"; 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 AtomicBoolean raiseOnEmptyMetrics = new AtomicBoolean(false); + 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) { @@ -79,8 +78,6 @@ public void addDimension(software.amazon.lambda.powertools.metrics.model.Dimensi dimensionSet.getDimensions().forEach((key, val) -> { try { emfDimensionSet.addDimension(key, val); - // Update our local copy of default dimensions - defaultDimensions.put(key, val); } catch (Exception e) { // Ignore dimension errors } @@ -91,7 +88,8 @@ public void addDimension(software.amazon.lambda.powertools.metrics.model.Dimensi @Override public void addMetadata(String key, Object value) { - emfLogger.putMetadata(key, value); + emfLogger.putProperty(key, value); + properties.put(key, value); } @Override @@ -173,45 +171,13 @@ public void flush() { public void captureColdStartMetric(Context context, software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { if (isColdStart()) { - if (isMetricsDisabled()) { - LOGGER.debug("Metrics are disabled, skipping cold start metric capture"); - return; - } - - Validator.validateNamespace(namespace); - - software.amazon.cloudwatchlogs.emf.logger.MetricsLogger coldStartLogger = new software.amazon.cloudwatchlogs.emf.logger.MetricsLogger(); - - try { - coldStartLogger.setNamespace(namespace); - } catch (Exception e) { - LOGGER.error("Namespace cannot be set for cold start metrics due to an error in EMF", e); - } - - coldStartLogger.putMetric(COLD_START_METRIC, 1, Unit.COUNT); - - // Set dimensions if provided - if (dimensions != null) { - DimensionSet emfDimensionSet = new DimensionSet(); - dimensions.getDimensions().forEach((key, val) -> { - try { - emfDimensionSet.addDimension(key, val); - } catch (Exception e) { - // Ignore dimension errors - } - }); - coldStartLogger.setDimensions(emfDimensionSet); - } - - // Add request ID from context if available - if (context != null && context.getAwsRequestId() != null) { - coldStartLogger.putProperty(REQUEST_ID_PROPERTY, context.getAwsRequestId()); - } - - // Add trace ID using the standard logic - getXrayTraceId().ifPresent(traceId -> coldStartLogger.putProperty(TRACE_ID_PROPERTY, traceId)); - - coldStartLogger.flush(); + flushMetrics(metrics -> { + MetricsUtils.addRequestIdAndXrayTraceIdIfAvailable(context, metrics); + if (dimensions != null) { + metrics.setDefaultDimensions(dimensions); + } + metrics.addMetric(COLD_START_METRIC, 1, MetricUnit.COUNT); + }); } } @@ -221,43 +187,24 @@ public void captureColdStartMetric(software.amazon.lambda.powertools.metrics.mod } @Override - public void flushSingleMetric(String name, double value, MetricUnit unit, String namespace, - software.amazon.lambda.powertools.metrics.model.DimensionSet dimensions) { + public void flushMetrics(Consumer<Metrics> metricsConsumer) { if (isMetricsDisabled()) { LOGGER.debug("Metrics are disabled, skipping single metric flush"); return; } - - Validator.validateNamespace(namespace); - - // Create a new logger for this single metric - software.amazon.cloudwatchlogs.emf.logger.MetricsLogger singleMetricLogger = new software.amazon.cloudwatchlogs.emf.logger.MetricsLogger( - environmentProvider); - - try { - singleMetricLogger.setNamespace(namespace); - } catch (Exception e) { - LOGGER.error("Namespace cannot be set for single metric due to an error in EMF", e); + // Create a new instance, inheriting namespace, default dimensions, and metadata + EmfMetricsLogger metrics = new EmfMetricsLogger(environmentProvider, new MetricsContext()); + if (namespace != null) { + metrics.setNamespace(this.namespace); } - - // Add the metric - singleMetricLogger.putMetric(name, value, convertUnit(unit)); - - // Set dimensions if provided - if (dimensions != null) { - DimensionSet emfDimensionSet = new DimensionSet(); - dimensions.getDimensions().forEach((key, val) -> { - try { - emfDimensionSet.addDimension(key, val); - } catch (Exception e) { - // Ignore dimension errors - } - }); - singleMetricLogger.setDimensions(emfDimensionSet); + if (!defaultDimensions.isEmpty()) { + metrics.setDefaultDimensions(software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions)); } + properties.forEach(metrics::addMetadata); + + metricsConsumer.accept(metrics); - // Flush the metric - singleMetricLogger.flush(); + metrics.flush(); } private boolean isMetricsDisabled() { 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 32824e24f..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 @@ -34,8 +34,6 @@ @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 SERVICE_DIMENSION = "Service"; private static final String FUNCTION_NAME_ENV_VAR = "POWERTOOLS_METRICS_FUNCTION_NAME"; @@ -90,11 +88,10 @@ public Object around(ProceedingJoinPoint pjp, metricsInstance.setRaiseOnEmptyMetrics(metrics.raiseOnEmptyMetrics()); - // Add trace ID metadata if available - LambdaHandlerProcessor.getXrayTraceId() - .ifPresent(traceId -> metricsInstance.addMetadata(TRACE_ID_PROPERTY, traceId)); + Context extractedContext = extractContext(pjp); + MetricsUtils.addRequestIdAndXrayTraceIdIfAvailable(extractedContext, metricsInstance); - captureColdStartMetricIfEnabled(extractContext(pjp), metrics); + captureColdStartMetricIfEnabled(extractedContext, metrics); try { return pjp.proceed(proceedArgs); @@ -107,40 +104,36 @@ public Object around(ProceedingJoinPoint pjp, return pjp.proceed(proceedArgs); } - private void captureColdStartMetricIfEnabled(Context extractedContext, FlushMetrics metrics) { + private void captureColdStartMetricIfEnabled(Context extractedContext, FlushMetrics flushMetrics) { if (extractedContext == null) { return; } - Metrics metricsInstance = MetricsFactory.getMetricsInstance(); - // This can be null e.g. during unit tests when mocking the Lambda context - if (extractedContext.getAwsRequestId() != null) { - metricsInstance.addMetadata(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); - } + Metrics metrics = MetricsFactory.getMetricsInstance(); // Only capture cold start metrics if enabled on annotation - if (metrics.captureColdStart()) { + if (flushMetrics.captureColdStart()) { // Get function name from annotation or context - String funcName = functionName(metrics, extractedContext); + String funcName = functionName(flushMetrics, extractedContext); - DimensionSet coldStartDimensions = new DimensionSet(); + DimensionSet dimensionSet = new DimensionSet(); // Get service name from metrics instance default dimensions or fallback - String serviceName = metricsInstance.getDefaultDimensions().getDimensions().getOrDefault( + String serviceName = metrics.getDefaultDimensions().getDimensions().getOrDefault( SERVICE_DIMENSION, - serviceNameWithFallback(metrics)); + serviceNameWithFallback(flushMetrics)); // Only add service if it is not undefined if (!LambdaConstants.SERVICE_UNDEFINED.equals(serviceName)) { - coldStartDimensions.addDimension(SERVICE_DIMENSION, serviceName); + dimensionSet.addDimension(SERVICE_DIMENSION, serviceName); } // Add function name if (funcName != null) { - coldStartDimensions.addDimension("FunctionName", funcName); + dimensionSet.addDimension("FunctionName", funcName); } - metricsInstance.captureColdStartMetric(extractedContext, coldStartDimensions); + metrics.captureColdStartMetric(extractedContext, dimensionSet); } } } 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/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java index 6c324221c..9f793f977 100644 --- 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 @@ -246,7 +246,7 @@ void shouldAddDimensionSet() throws Exception { @Test void shouldThrowExceptionWhenDimensionSetIsNull() { // When/Then - assertThatThrownBy(() -> metrics.addDimension((DimensionSet) null)) + assertThatThrownBy(() -> metrics.addDimension(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("DimensionSet cannot be null"); } @@ -263,8 +263,8 @@ void shouldAddMetadata() throws Exception { JsonNode rootNode = objectMapper.readTree(emfOutput); // The metadata is added to the _aws section in the EMF output - assertThat(rootNode.get("_aws").has("CustomMetadata")).isTrue(); - assertThat(rootNode.get("_aws").get("CustomMetadata").asText()).isEqualTo("MetadataValue"); + assertThat(rootNode.has("CustomMetadata")).isTrue(); + assertThat(rootNode.get("CustomMetadata").asText()).isEqualTo("MetadataValue"); } @Test @@ -304,7 +304,7 @@ void shouldGetDefaultDimensions() { @Test void shouldThrowExceptionWhenDefaultDimensionSetIsNull() { // When/Then - assertThatThrownBy(() -> metrics.setDefaultDimensions((DimensionSet) null)) + assertThatThrownBy(() -> metrics.setDefaultDimensions(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("DimensionSet cannot be null"); } @@ -346,7 +346,7 @@ void shouldLogWarningOnEmptyMetrics() throws Exception { // Then // Read the log file and check for the warning - String logContent = new String(Files.readAllBytes(logFile.toPath()), StandardCharsets.UTF_8); + 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(); @@ -446,6 +446,37 @@ void shouldReuseNamespaceForColdStartMetric() throws Exception { .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 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 index 949828a13..4a2e33a78 100644 --- 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 @@ -2,6 +2,7 @@ import java.time.Instant; import java.util.Collections; +import java.util.function.Consumer; import com.amazonaws.services.lambda.runtime.Context; @@ -77,6 +78,11 @@ 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) { From 89f7b97c845ceff2b209c0d9994deda7672fc34d Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 2 Oct 2025 13:42:35 +0200 Subject: [PATCH 0863/1008] Update docs javascript. (#2169) --- docs/javascript/aws-amplify.min.js | 108 ----------------------------- docs/javascript/extra.js | 76 -------------------- mkdocs.yml | 5 +- 3 files changed, 2 insertions(+), 187 deletions(-) delete mode 100644 docs/javascript/aws-amplify.min.js delete mode 100644 docs/javascript/extra.js 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/mkdocs.yml b/mkdocs.yml index 372dfedd7..c75fa90e7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ 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.powertools.aws.dev/lambda/java/latest/ nav: - Homepage: index.md - Changelog: changelog.md @@ -122,9 +122,8 @@ plugins: 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: From 07e8948f470c9980df640f2b9b6e6ae5c15d3e7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:19:46 +0200 Subject: [PATCH 0864/1008] chore: bump aws.sdk.version from 2.33.2 to 2.34.5 (#2156) Bumps `aws.sdk.version` from 2.33.2 to 2.34.5. Updates `software.amazon.awssdk:bom` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:http-client-spi` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.34.5 Updates `software.amazon.awssdk:dynamodb` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.34.5 Updates `software.amazon.awssdk:lambda` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.34.5 Updates `software.amazon.awssdk:cloudwatch` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:xray` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.34.5 Updates `software.amazon.awssdk:cloudformation` from 2.33.2 to 2.34.5 Updates `software.amazon.awssdk:sts` from 2.33.2 to 2.34.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.34.5 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.34.5 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.34.5 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index eb762665b..fe2c109fb 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.33.5</aws.sdk.version> + <aws.sdk.version>2.34.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index cb14997c4..f31bcad70 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.1</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.33.5</aws.sdk.version> + <aws.sdk.version>2.34.5</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 9bc7c5e37..4145d4753 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.33.5</aws.sdk.version> + <aws.sdk.version>2.34.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From a22ab8875cf87e0bff07733c07aa281433972ac9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:20:24 +0200 Subject: [PATCH 0865/1008] chore: bump com.amazonaws:aws-lambda-java-runtime-interface-client (#2149) Bumps [com.amazonaws:aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs) from 2.8.3 to 2.8.6. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-runtime-interface-client dependency-version: 2.8.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index fe2c109fb..c437bbe9e 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -77,7 +77,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.3</version> + <version>2.8.6</version> </dependency> </dependencies> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index a47be2458..a366097b3 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.3</version> + <version>2.8.6</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 7b34c94c2..956b8a9d9 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -43,7 +43,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.3</version> + <version>2.8.6</version> </dependency> <dependency> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 3bd5a5de7..d5aab7ee6 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.3</version> + <version>2.8.6</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index a85e466fe..636435fbb 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.3</version> + <version>2.8.6</version> </dependency> </dependencies> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 4145d4753..03188d2c4 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -120,7 +120,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.3</version> + <version>2.8.6</version> </dependency> </dependencies> </dependencyManagement> From 13499e8bef0335c82af003f84517d1962dc08cca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:21:16 +0200 Subject: [PATCH 0866/1008] chore: bump io.github.ascopes:protobuf-maven-plugin from 3.9.0 to 3.10.0 (#2155) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.9.0 to 3.10.0. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.9.0...v3.10.0) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 7f9449361..8f2f16d0a 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.9.0</version> + <version>3.10.0</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index b0705a204..cac2706b7 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -181,7 +181,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.9.0</version> + <version>3.10.0</version> <executions> <execution> <id>generate-test-sources</id> From e8d0d784ef8361af463d7e95398847c084ff7384 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:21:32 +0200 Subject: [PATCH 0867/1008] chore: bump github/codeql-action from 3.30.1 to 3.30.5 (#2157) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.1 to 3.30.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f1f6e5f6af878fb37288ce1c627459e94dbf7d01...3599b3baa15b485a2e49ef411a7a4bb2452e7f93) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.30.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 8ff72b365..83c5e64d1 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.29.5 + uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5 with: sarif_file: results.sarif From a770e9a09db1db0c6eb1e2629bd4c7a35f891692 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:21:52 +0200 Subject: [PATCH 0868/1008] chore: bump actions/dependency-review-action from 4.7.3 to 4.8.0 (#2158) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.7.3 to 4.8.0. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/595b5aeba73380359d98a5e087f648dbb0edce1b...56339e523c0409420f6c2c9a2f4292bbb3c07dd3) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 903b96af9..74b9188c9 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -26,6 +26,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents - uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3 + uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 with: config-file: './.github/dependency-review-config.yml' From 66c57a644eb5f20bb37b46e55a9cf7f80ae23a7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:22:21 +0200 Subject: [PATCH 0869/1008] chore: bump org.apache.maven.plugins:maven-failsafe-plugin (#2159) Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.5.3 to 3.5.4. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.3...surefire-3.5.4) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index a0d500686..32ea35d36 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -214,7 +214,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <executions> <execution> <goals> @@ -241,7 +241,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <executions> <execution> <goals> From 5f3c5de0d98cce1f049bed378e46df6243dffdc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:23:15 +0200 Subject: [PATCH 0870/1008] chore: bump log4j.version from 2.25.1 to 2.25.2 (#2160) Bumps `log4j.version` from 2.25.1 to 2.25.2. Updates `org.apache.logging.log4j:log4j-core` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-api` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-layout-template-json` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-jcl` from 2.25.1 to 2.25.2 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j-impl dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-layout-template-json dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-jcl dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index db3750e53..677a2b4b6 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -11,7 +11,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.25.1</log4j.version> + <log4j.version>2.25.2</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index a366097b3..64649fe45 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -9,7 +9,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.25.1</log4j.version> + <log4j.version>2.25.2</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index d5aab7ee6..45d272f70 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -8,7 +8,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> <properties> - <log4j.version>2.25.1</log4j.version> + <log4j.version>2.25.2</log4j.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> diff --git a/pom.xml b/pom.xml index f31bcad70..51ae56027 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ <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.1</log4j.version> + <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> <aws.sdk.version>2.34.5</aws.sdk.version> From f37a4a4cb2f65bd78efaa59a1f11a8d4cdf34090 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:23:33 +0200 Subject: [PATCH 0871/1008] chore: bump squidfunk/mkdocs-material in /docs (#2164) Bumps squidfunk/mkdocs-material from `86d21da` to `00f9276`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 00f9276315990b82f5af8c47bb2b71e2c69aef9f02a08f8dffd2515f42310753 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 696d43a12..fe4f2ec55 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:86d21da4f45f16e30774bf911e5b4795da13ce0cd197dbf8d3d059f256b2cc37 +FROM squidfunk/mkdocs-material@sha256:00f9276315990b82f5af8c47bb2b71e2c69aef9f02a08f8dffd2515f42310753 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 8a19a2971e827412e28155f92aded425ce722948 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:23:57 +0200 Subject: [PATCH 0872/1008] chore: bump ossf/scorecard-action from 2.4.2 to 2.4.3 (#2165) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.2 to 2.4.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/05b42c624433fc40578a4040d5cf5e36ddca8cde...4eaacf0543bb3f2c246792bd56e8cdeffafb205a) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-version: 2.4.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 83c5e64d1..243d2f523 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -39,7 +39,7 @@ jobs: with: persist-credentials: false - name: Run Analysis - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif From 7762b86771a896377ed12a840894db77253ecd5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:11:51 +0200 Subject: [PATCH 0873/1008] chore: bump graalvm/setup-graalvm from 1.3.6 to 1.4.1 (#2168) Bumps [graalvm/setup-graalvm](https://github.com/graalvm/setup-graalvm) from 1.3.6 to 1.4.1. - [Release notes](https://github.com/graalvm/setup-graalvm/releases) - [Commits](https://github.com/graalvm/setup-graalvm/compare/7a1da54cb7fdef4ea19f6ecdfa9ecf59dc5a48fe...2a2412009026a83f51d179f92dc2b3fd4c8142df) --- updated-dependencies: - dependency-name: graalvm/setup-graalvm dependency-version: 1.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 09a232188..0167925f4 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -102,7 +102,7 @@ jobs: powertools-*/** pom.xml - name: Setup GraalVM - uses: graalvm/setup-graalvm@7a1da54cb7fdef4ea19f6ecdfa9ecf59dc5a48fe # v1.3.6 + uses: graalvm/setup-graalvm@2a2412009026a83f51d179f92dc2b3fd4c8142df # v1.4.1 with: java-version: "21" distribution: "graalvm" From 53adbe25b1f4b6d5000f326a52f271e661e0ab9d Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 3 Oct 2025 11:18:45 +0200 Subject: [PATCH 0874/1008] chore(docs): Add AWS docs meta tags (#2170) * Add docs meta tags. * Use full Powertools names. --- docs/overrides/main.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/overrides/main.html b/docs/overrides/main.html index e4c38e21b..5f7a59c02 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -6,3 +6,8 @@ <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 %} From 267f009897671274adc0b8939899ab4f976e4fa4 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 7 Oct 2025 11:09:52 +0200 Subject: [PATCH 0875/1008] chore: Add User-Agent execution interceptors (#2166) --- powertools-batch/pom.xml | 23 ++++++--- .../internal/BatchUserAgentInterceptor.java | 37 ++++++++++++++ .../global/handlers/execution.interceptors | 1 + .../BatchUserAgentInterceptorTest.java | 34 +++++++++++++ powertools-cloudformation/pom.xml | 14 ++++++ .../CloudformationUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../resource-config.json | 2 + .../global/handlers/execution.interceptors | 1 + ...loudformationUserAgentInterceptorTest.java | 34 +++++++++++++ .../internal/UserAgentConfigurator.java | 49 +++++++++++++------ .../internal/UserAgentConfiguratorTest.java | 39 ++++++++++++--- .../powertools-idempotency-dynamodb/pom.xml | 9 ++++ ...empotencyDynamodbUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../global/handlers/execution.interceptors | 1 + ...tencyDynamodbUserAgentInterceptorTest.java | 34 +++++++++++++ powertools-kafka/pom.xml | 15 ++++++ .../internal/KafkaUserAgentInterceptor.java | 37 ++++++++++++++ .../global/handlers/execution.interceptors | 1 + .../KafkaUserAgentInterceptorTest.java | 34 +++++++++++++ .../LargeMessagesUserAgentInterceptor.java | 37 ++++++++++++++ .../global/handlers/execution.interceptors | 1 + ...LargeMessagesUserAgentInterceptorTest.java | 34 +++++++++++++ .../powertools-logging-log4j/pom.xml | 10 ++++ .../internal/Log4jUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../resource-config.json | 2 + .../global/handlers/execution.interceptors | 1 + .../Log4jUserAgentInterceptorTest.java | 34 +++++++++++++ .../powertools-logging-logback/pom.xml | 10 ++++ .../internal/LogbackUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../resource-config.json | 2 + .../global/handlers/execution.interceptors | 1 + .../LogbackUserAgentInterceptorTest.java | 34 +++++++++++++ powertools-metrics/pom.xml | 10 ++++ .../internal/MetricsUserAgentInterceptor.java | 37 ++++++++++++++ .../powertools-metrics/reflect-config.json | 4 ++ .../powertools-metrics/resource-config.json | 3 ++ .../global/handlers/execution.interceptors | 1 + .../MetricsUserAgentInterceptorTest.java | 34 +++++++++++++ .../powertools-parameters-appconfig/pom.xml | 9 ++++ ...rametersAppconfigUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../global/handlers/execution.interceptors | 1 + ...tersAppconfigUserAgentInterceptorTest.java | 34 +++++++++++++ .../powertools-parameters-dynamodb/pom.xml | 9 ++++ ...arametersDynamodbUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../global/handlers/execution.interceptors | 1 + ...etersDynamodbUserAgentInterceptorTest.java | 34 +++++++++++++ .../powertools-parameters-secrets/pom.xml | 9 ++++ ...ParametersSecretsUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../global/handlers/execution.interceptors | 1 + ...metersSecretsUserAgentInterceptorTest.java | 34 +++++++++++++ .../powertools-parameters-ssm/pom.xml | 9 ++++ .../ParametersSsmUserAgentInterceptor.java | 37 ++++++++++++++ .../reflect-config.json | 4 ++ .../global/handlers/execution.interceptors | 1 + ...ParametersSsmUserAgentInterceptorTest.java | 34 +++++++++++++ powertools-tracing/pom.xml | 9 ++++ .../internal/TracingUserAgentInterceptor.java | 37 ++++++++++++++ .../powertools-tracing/reflect-config.json | 4 ++ .../powertools-tracing/resource-config.json | 2 + .../global/handlers/execution.interceptors | 1 + .../TracingUserAgentInterceptorTest.java | 34 +++++++++++++ powertools-validation/pom.xml | 10 ++++ .../ValidationUserAgentInterceptor.java | 37 ++++++++++++++ .../global/handlers/execution.interceptors | 1 + .../ValidationUserAgentInterceptorTest.java | 34 +++++++++++++ 72 files changed, 1263 insertions(+), 30 deletions(-) create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptor.java create mode 100644 powertools-batch/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-batch/src/test/java/software/amazon/lambda/powertools/batch/internal/BatchUserAgentInterceptorTest.java create mode 100644 powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptor.java create mode 100644 powertools-cloudformation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/internal/CloudformationUserAgentInterceptorTest.java create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptor.java create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/dynamodb/internal/IdempotencyDynamodbUserAgentInterceptorTest.java create mode 100644 powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptor.java create mode 100644 powertools-kafka/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/internal/KafkaUserAgentInterceptorTest.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptor.java create mode 100644 powertools-large-messages/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessagesUserAgentInterceptorTest.java create mode 100644 powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptor.java create mode 100644 powertools-logging/powertools-logging-log4j/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-logging/powertools-logging-log4j/src/test/java/software/amazon/lambda/powertools/logging/log4j/internal/Log4jUserAgentInterceptorTest.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptor.java create mode 100644 powertools-logging/powertools-logging-logback/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/logback/internal/LogbackUserAgentInterceptorTest.java create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptor.java create mode 100644 powertools-metrics/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/MetricsUserAgentInterceptorTest.java create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptor.java create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/internal/ParametersAppconfigUserAgentInterceptorTest.java create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptor.java create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/internal/ParametersDynamodbUserAgentInterceptorTest.java create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptor.java create mode 100644 powertools-parameters/powertools-parameters-secrets/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/internal/ParametersSecretsUserAgentInterceptorTest.java create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptor.java create mode 100644 powertools-parameters/powertools-parameters-ssm/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/internal/ParametersSsmUserAgentInterceptorTest.java create mode 100644 powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptor.java create mode 100644 powertools-tracing/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/TracingUserAgentInterceptorTest.java create mode 100644 powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptor.java create mode 100644 powertools-validation/src/main/resources/software/amazon/awssdk/global/handlers/execution.interceptors create mode 100644 powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationUserAgentInterceptorTest.java diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index b389df6fe..850be6922 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -50,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> @@ -57,12 +67,6 @@ <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-simple</artifactId> - <version>2.0.17</version> - <scope>test</scope> - </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> @@ -83,6 +87,11 @@ <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/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/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/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-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index ca4e7536f..d49fcc2bc 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -42,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> @@ -58,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,6 +111,11 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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 index 218382888..092ef9b54 100644 --- 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 @@ -428,5 +428,9 @@ { "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 index f3b58337b..cbbfb270d 100644 --- 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 @@ -30,6 +30,8 @@ "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", 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/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-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java index d2e592902..7deca89f1 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java @@ -18,26 +18,27 @@ import java.io.IOException; 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 final String POWERTOOLS_VERSION = getProjectVersion(); - private static final String USER_AGENT_PATTERN = "PT/" + PT_FEATURE_VARIABLE + "/" + POWERTOOLS_VERSION + " PTEnv/" + private static final String USER_AGENT_PATTERN = "PT/" + PT_FEATURE_VARIABLE + "/" + POWERTOOLS_VERSION + " PTENV/" + PT_EXEC_ENV_VARIABLE; private UserAgentConfigurator() { @@ -53,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. @@ -64,28 +64,46 @@ static String getProjectVersion() { * @return the version of the project as configured in the given properties file */ static String getVersionFromProperties(String propertyFileName, String versionKey) { - - InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(propertyFileName); - - if (is != null) { - try { + try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(propertyFileName)) { + if (is != null) { Properties properties = new Properties(); 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. + * If the property is already set and not empty, appends the Powertools user agent with a "/" separator. + * 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); + + if (existingValue != null && !existingValue.isEmpty()) { + System.setProperty(SDK_USER_AGENT_APP_ID, existingValue + "/" + powertoolsUserAgent); + } else { + System.setProperty(SDK_USER_AGENT_APP_ID, powertoolsUserAgent); + } + } 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); + } + } + /** * 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,7 +114,6 @@ 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 = USER_AGENT_PATTERN.replace(PT_EXEC_ENV_VARIABLE, ptExecEnv); @@ -105,7 +122,7 @@ public static String getUserAgent(String ptFeature) { 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-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 index 0c7935a55..fbe4529d8 100644 --- 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 @@ -31,11 +31,9 @@ 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 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() { @@ -91,7 +89,7 @@ void testGetUserAgent() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/test-feature/" + VERSION + " PTEnv/NA"); + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); } @@ -101,7 +99,7 @@ void testGetUserAgent_NoFeature() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA"); + .isEqualTo("PT/NO-OP/" + VERSION + " PTENV/NA"); } @Test @@ -110,7 +108,7 @@ void testGetUserAgent_NullFeature() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA"); + .isEqualTo("PT/NO-OP/" + VERSION + " PTENV/NA"); } @Test @@ -120,7 +118,34 @@ void testGetUserAgent_SetAWSExecutionEnv() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/test-feature/" + VERSION + " PTEnv/AWS_Lambda_java8"); + .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_WithExistingValue() { + 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_WithEmptyExistingValue() { + System.setProperty("sdk.ua.appId", ""); + UserAgentConfigurator.configureUserAgent("test-feature"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); } } diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 191d75eeb..885c9c9cd 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -57,6 +57,10 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>org.crac</groupId> <artifactId>crac</artifactId> @@ -94,6 +98,11 @@ <version>2.2.0</version> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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 index f0ba9c4c2..6d6fbceb3 100644 --- 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 @@ -321,5 +321,9 @@ { "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/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-kafka/pom.xml b/powertools-kafka/pom.xml index cac2706b7..65cd7051d 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -78,6 +78,16 @@ <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> <!-- Test dependencies --> <dependency> @@ -120,6 +130,11 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> 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/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/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-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/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-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 92283b0cf..2b85a3752 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -39,6 +39,11 @@ <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> @@ -93,6 +98,11 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> <profile> 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/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 index adbd9e0c1..6cbbee583 100644 --- 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 @@ -1083,5 +1083,9 @@ "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 index cf017fdeb..a4bcd55fa 100644 --- 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 @@ -34,6 +34,8 @@ "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/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/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-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index ff0312001..17e39dda7 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -36,6 +36,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + <scope>provided</scope> + </dependency> <!-- Test dependencies --> <dependency> @@ -90,6 +95,11 @@ <artifactId>junit-pioneer</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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 index dfc50427f..d4f4a4d7d 100644 --- 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 @@ -179,5 +179,9 @@ "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 index 33d1d61c4..b60a39ad5 100644 --- 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 @@ -10,6 +10,8 @@ "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/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/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-metrics/pom.xml b/powertools-metrics/pom.xml index c1ac723f1..58eab8a45 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -73,6 +73,11 @@ <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 --> <dependency> @@ -117,6 +122,11 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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 index 43b2822d6..533335782 100644 --- 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 @@ -148,5 +148,9 @@ "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 index d47298855..0667ee26a 100644 --- 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 @@ -6,6 +6,9 @@ }, { "pattern": "\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, + { + "pattern": "\\Qsoftware/amazon/awssdk/global/handlers/execution.interceptors\\E" } ] }, 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/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-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index bbb6a1111..b2c4f3426 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -35,6 +35,10 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> @@ -82,6 +86,11 @@ <artifactId>aspectjweaver</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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 index 7e7b40197..b9ae934d6 100644 --- 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 @@ -361,5 +361,9 @@ { "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/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/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 index fa969e585..e03227eb8 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -36,6 +36,10 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> @@ -83,6 +87,11 @@ <artifactId>aspectjweaver</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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 index 77e953aac..e49454b83 100644 --- 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 @@ -322,5 +322,9 @@ { "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/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/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 index 55c254f88..6a576f529 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -36,6 +36,10 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> @@ -83,6 +87,11 @@ <artifactId>aspectjweaver</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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 index 7dfaf1b03..097a30784 100644 --- 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 @@ -319,5 +319,9 @@ { "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/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/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 index d1dc88d84..7758fd906 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -36,6 +36,10 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> @@ -83,6 +87,11 @@ <artifactId>aspectjweaver</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> 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/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 index 5bc2deb4a..a655e62ce 100644 --- 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 @@ -337,5 +337,9 @@ { "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/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/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-tracing/pom.xml b/powertools-tracing/pom.xml index 6081abd66..045e76cbf 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -46,6 +46,10 @@ <groupId>software.amazon.awssdk</groupId> <artifactId>aws-core</artifactId> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>sdk-core</artifactId> + </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> @@ -118,6 +122,11 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/reflect-config.json b/powertools-tracing/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing/reflect-config.json index 94a514dee..a71154276 100644 --- 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 @@ -361,5 +361,9 @@ { "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 index 2ac72d18f..64408b8b6 100644 --- 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 @@ -26,6 +26,8 @@ "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/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-validation/pom.xml b/powertools-validation/pom.xml index 1c048e66a..6ecee16a1 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -75,6 +75,11 @@ <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> @@ -122,6 +127,11 @@ <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>s3</artifactId> + <scope>test</scope> + </dependency> </dependencies> <profiles> 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/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/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/"); + } +} From 3e7ed05aabc56270ff43e41e9512a6181ccb7588 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:48:41 +0200 Subject: [PATCH 0876/1008] chore: bump org.apache.maven.plugins:maven-artifact-plugin (#2171) Bumps [org.apache.maven.plugins:maven-artifact-plugin](https://github.com/apache/maven-artifact-plugin) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/apache/maven-artifact-plugin/releases) - [Commits](https://github.com/apache/maven-artifact-plugin/compare/maven-artifact-plugin-3.6.0...maven-artifact-plugin-3.6.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-artifact-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 51ae56027..801036a2e 100644 --- a/pom.xml +++ b/pom.xml @@ -466,7 +466,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-artifact-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <configuration> <reproducible>true</reproducible> </configuration> From 3273f4fc6fd18e36d9fabf9b6d716433295e6874 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:48:54 +0200 Subject: [PATCH 0877/1008] chore: bump org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0 (#2172) Bumps org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.19.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 801036a2e..9084e0b1c 100644 --- a/pom.xml +++ b/pom.xml @@ -310,7 +310,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.18.0</version> + <version>3.19.0</version> </dependency> <!-- Test dependencies --> From 44ed8c29f27797ea44f2caa44108e92807d944ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:49:05 +0200 Subject: [PATCH 0878/1008] chore: bump aws.sdk.version from 2.34.5 to 2.34.9 (#2174) Bumps `aws.sdk.version` from 2.34.5 to 2.34.9. Updates `software.amazon.awssdk:bom` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:http-client-spi` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.34.9 Updates `software.amazon.awssdk:dynamodb` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.34.9 Updates `software.amazon.awssdk:lambda` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.34.9 Updates `software.amazon.awssdk:cloudwatch` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:xray` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.34.9 Updates `software.amazon.awssdk:cloudformation` from 2.34.5 to 2.34.9 Updates `software.amazon.awssdk:sts` from 2.34.5 to 2.34.9 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.34.9 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.34.9 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.34.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c437bbe9e..a1f4aadc3 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.34.5</aws.sdk.version> + <aws.sdk.version>2.34.9</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 9084e0b1c..133e7d88c 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.34.5</aws.sdk.version> + <aws.sdk.version>2.34.9</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 03188d2c4..201f38c7f 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.34.5</aws.sdk.version> + <aws.sdk.version>2.34.9</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 26c80fc47b2c0cb5c69571cd8f352110555fc0c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:49:17 +0200 Subject: [PATCH 0879/1008] chore: bump com.google.protobuf:protobuf-java from 4.32.0 to 4.32.1 (#2175) Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 4.32.0 to 4.32.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-version: 4.32.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 8f2f16d0a..8a0ac2d89 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -12,7 +12,7 @@ <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.32.0</protobuf.version> + <protobuf.version>4.32.1</protobuf.version> </properties> <dependencies> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 65cd7051d..b1f1afd3e 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -36,7 +36,7 @@ <properties> <kafka-clients.version>4.1.0</kafka-clients.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.32.0</protobuf.version> + <protobuf.version>4.32.1</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> From f3689c89b875276e977e8efa641d0e84fc61c00a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:49:32 +0200 Subject: [PATCH 0880/1008] chore: bump aws-actions/configure-aws-credentials from 5.0.0 to 5.1.0 (#2177) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/a03048d87541d1d9fcf2ecf528a4a65ba9bd7838...00943011d9042930efac3dcd3a170e4273319bc8) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-version: 5.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index a9d3fbf7b..8cef6040e 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -41,7 +41,7 @@ jobs: 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@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 + uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index f18964852..a3c4a7542 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -64,7 +64,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0 + uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 @@ -97,7 +97,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 # v5.0.0 + uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61dd6234d..d1f094f86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -281,7 +281,7 @@ jobs: 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@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 + uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} From e4078ea0a8b9519a7284201e6d587d6251ae2b8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:50:13 +0200 Subject: [PATCH 0881/1008] chore: bump github/codeql-action from 3.30.5 to 4.30.8 (#2179) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.5 to 4.30.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/3599b3baa15b485a2e49ef411a7a4bb2452e7f93...f443b600d91635bebf5b0d9ebc620189c0d6fba5) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.30.8 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 243d2f523..bd9f16d74 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5 + uses: github/codeql-action/upload-sarif@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v3.29.5 with: sarif_file: results.sarif From 0a99d5166715119f28079d527889f51d2390de95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:54:40 +0200 Subject: [PATCH 0882/1008] chore: bump actions/dependency-review-action from 4.8.0 to 4.8.1 (#2180) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.8.0 to 4.8.1. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/56339e523c0409420f6c2c9a2f4292bbb3c07dd3...40c09b7dc99638e5ddb0bfd91c1673effc064d8a) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 74b9188c9..692acd64d 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -26,6 +26,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents - uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 + uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 with: config-file: './.github/dependency-review-config.yml' From bf621a6c79254cb3365803b67e20afcdaf53c17e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:11:09 +0200 Subject: [PATCH 0883/1008] chore: bump aws.sdk.version from 2.34.9 to 2.35.6 (#2183) Bumps `aws.sdk.version` from 2.34.9 to 2.35.6. Updates `software.amazon.awssdk:bom` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:http-client-spi` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.35.6 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.35.6 Updates `software.amazon.awssdk:dynamodb` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:lambda` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.35.6 Updates `software.amazon.awssdk:cloudwatch` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:xray` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.35.6 Updates `software.amazon.awssdk:cloudformation` from 2.34.9 to 2.35.6 Updates `software.amazon.awssdk:sts` from 2.34.9 to 2.35.6 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.35.6 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.35.6 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.35.6 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.35.6 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index a1f4aadc3..c5245e89f 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.34.9</aws.sdk.version> + <aws.sdk.version>2.35.6</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 133e7d88c..e639bdcce 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.34.9</aws.sdk.version> + <aws.sdk.version>2.35.6</aws.sdk.version> <aws.xray.recorder.version>2.19.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 201f38c7f..69c55842c 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.34.9</aws.sdk.version> + <aws.sdk.version>2.35.6</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From b1693e022562b20d066945445a663f391814069b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:11:26 +0200 Subject: [PATCH 0884/1008] chore: bump org.assertj:assertj-core from 3.27.4 to 3.27.6 (#2184) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.4 to 3.27.6. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.27.4...assertj-build-3.27.6) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e639bdcce..6d29d6d7e 100644 --- a/pom.xml +++ b/pom.xml @@ -336,7 +336,7 @@ <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> - <version>3.27.4</version> + <version>3.27.6</version> <scope>test</scope> <exclusions> <exclusion> From aadf172563ba14ae0da17bfd6b1116ce783bbd45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:13:00 +0200 Subject: [PATCH 0885/1008] chore: bump org.apache.maven.plugins:maven-javadoc-plugin (#2186) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.3 to 3.12.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.3...maven-javadoc-plugin-3.12.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d29d6d7e..8a74f6ec1 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ <maven-surefire-plugin.version>3.2.5</maven-surefire-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.11.3</maven-javadoc-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.1</maven-gpg-plugin.version> <aspectj-maven-plugin.version>1.14.1</aspectj-maven-plugin.version> From 66fbe5cd98b53eafd30ce170757fe21344b5d047 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 14 Oct 2025 17:15:25 +0200 Subject: [PATCH 0886/1008] chore(deps): Use mockito 5.20.0 (#2181) * chore(deps): Use mockito 5.20.0. * Remove non-applicable or not understandable TODOs. * fix pmd findings. * fix pmd findings. * Update protobuf generated classes after version bump. --- .../demo/kafka/protobuf/ProtobufProduct.java | 4 +- .../protobuf/ProtobufProductOrBuilder.java | 2 +- .../protobuf/ProtobufProductOuterClass.java | 6 +-- pom.xml | 25 +--------- .../lambda/powertools/e2e/Function.java | 49 +++++++++---------- .../persistence/BasePersistenceStore.java | 3 +- .../secrets/SecretsProviderBuilder.java | 1 - 7 files changed, 33 insertions(+), 57 deletions(-) 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 index 21961ad2b..e36810e08 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.32.0 +// Protobuf Java Version: 4.32.1 package org.demo.kafka.protobuf; @@ -19,7 +19,7 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 32, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", ProtobufProduct.class.getName()); } 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 index 347ea854a..951644b2f 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.32.0 +// Protobuf Java Version: 4.32.1 package org.demo.kafka.protobuf; 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 index 046283d9a..0da2eec57 100644 --- 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 @@ -1,19 +1,19 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.32.0 +// Protobuf Java Version: 4.32.1 package org.demo.kafka.protobuf; @com.google.protobuf.Generated -public final class ProtobufProductOuterClass { +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= */ 32, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", ProtobufProductOuterClass.class.getName()); } diff --git a/pom.xml b/pom.xml index 8a74f6ec1..a04780ba2 100644 --- a/pom.xml +++ b/pom.xml @@ -114,8 +114,8 @@ <aws.sdk.v1.version>1.12.781</aws.sdk.v1.version> <versions-maven-plugin.version>2.19.1</versions-maven-plugin.version> <elastic.version>1.7.0</elastic.version> - <mockito.version>5.19.0</mockito.version> - <mockito-junit-jupiter.version>5.19.0</mockito-junit-jupiter.version> + <mockito.version>5.20.0</mockito.version> + <mockito-junit-jupiter.version>5.20.0</mockito-junit-jupiter.version> <junit-pioneer.version>2.3.0</junit-pioneer.version> <crac.version>1.5.0</crac.version> @@ -536,12 +536,6 @@ <profiles> <profile> <id>release</id> - <!-- TODO: Revert once 5.19.1 is stable released. --> - <!-- https://github.com/aws-powertools/powertools-lambda-java/issues/2079 --> - <properties> - <mockito.version>5.19.0</mockito.version> - <mockito-junit-jupiter.version>5.19.0</mockito-junit-jupiter.version> - </properties> <build> <plugins> <plugin> @@ -643,21 +637,6 @@ </plugins> </build> </profile> - <profile> - <id>generate-graalvm-files</id> - <properties> - <mockito.version>5.19.1-SNAPSHOT</mockito.version> - <mockito-junit-jupiter.version>5.19.1-SNAPSHOT</mockito-junit-jupiter.version> - </properties> - </profile> - <profile> - <id>graalvm-native</id> - <properties> - <mockito.version>5.19.1-SNAPSHOT</mockito.version> - <mockito-junit-jupiter.version>5.19.1-SNAPSHOT</mockito-junit-jupiter.version> - </properties> - </profile> - </profiles> </project> 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 bfde65bc8..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,15 +14,6 @@ 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.DynamodbEvent; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent; -import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -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 java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,8 +21,20 @@ 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; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +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 software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; @@ -39,7 +42,6 @@ import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; import software.amazon.lambda.powertools.e2e.model.Product; - public class Function implements RequestHandler<InputStream, Object> { private static final Logger LOGGER = LoggerFactory.getLogger(Function.class); @@ -48,7 +50,6 @@ public class Function implements RequestHandler<InputStream, Object> { private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> kinesisHandler; private final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> ddbHandler; private final String ddbOutputTable; - private DynamoDbClient ddbClient; public Function() { sqsHandler = new BatchMessageHandlerBuilder() @@ -69,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() @@ -94,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(); @@ -118,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.toString()); 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); @@ -148,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-idempotency/powertools-idempotency-core/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 index 8b08434ba..7e93dc00c 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -132,7 +132,6 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { updateRecord(dataRecord); saveToCache(dataRecord); } catch (JsonProcessingException e) { - // TODO : throw ? throw new RuntimeException("Error while serializing the response", e); } } @@ -405,7 +404,7 @@ void configure(IdempotencyConfig config, String functionName, LRUCache<String, D private static boolean isEqual(String dataRecordPayload, String dataHash) { if (dataHash != null && dataRecordPayload != null) { - return dataHash.length() != dataRecordPayload.length() ? false : dataHash.equals(dataRecordPayload); + return dataHash.length() == dataRecordPayload.length() && dataHash.equals(dataRecordPayload); } else { return false; } 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 index c5806689f..517274e19 100644 --- 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 @@ -52,7 +52,6 @@ private static SecretsManagerClient createClient() { */ public SecretsProvider build() { if (cacheManager == null) { - // TODO - what should we do with this cacheManager = new CacheManager(); } SecretsProvider provider; From 13d54ff5468e9849607345a9bb92c14d4c6db620 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 14 Oct 2025 17:39:46 +0200 Subject: [PATCH 0887/1008] improv: Make trace id access more robust. (#2188) * improv: Make trace id access more robust. * Update comments. --- .../reflect-config.json | 4 +- powertools-common/pom.xml | 4 ++ .../common/internal/LambdaConstants.java | 7 ++- .../internal/LambdaHandlerProcessor.java | 43 ++++++++++++------- .../internal/LambdaHandlerProcessorTest.java | 26 +++++++++++ .../reflect-config.json | 2 +- .../PowerToolsResolverFactoryTest.java | 2 +- .../reflect-config.json | 2 +- .../internal/LambdaEcsEncoderTest.java | 2 +- .../internal/LambdaJsonEncoderTest.java | 2 +- .../powertools-logging/reflect-config.json | 2 +- .../internal/LambdaLoggingAspectTest.java | 4 +- .../powertools-metrics/reflect-config.json | 2 +- .../metrics/ConfigurationPrecedenceTest.java | 6 +-- .../metrics/MetricsFactoryTest.java | 6 +-- .../metrics/RequestHandlerTest.java | 6 +-- .../internal/EmfMetricsLoggerTest.java | 6 +-- .../internal/LambdaMetricsAspectTest.java | 6 +-- .../powertools-tracing/reflect-config.json | 2 +- .../internal/LambdaTracingAspectTest.java | 2 +- 20 files changed, 91 insertions(+), 45 deletions(-) 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 index 962962055..8d3f375f2 100644 --- 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 @@ -33,6 +33,6 @@ }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", - "fields":[{"name":"IS_COLD_START"},{"name":"SERVICE_NAME"}] + "fields":[{"name":"isColdStart"},{"name":"serviceName"}] } -] \ No newline at end of file +] diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 7620d84ee..d68bcb63a 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -44,6 +44,10 @@ <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> diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java index d27ac1aa2..69fc1283a 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java @@ -14,11 +14,16 @@ package software.amazon.lambda.powertools.common.internal; -public class LambdaConstants { +public final class LambdaConstants { + private LambdaConstants() { + // Constant holder class + } + public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME"; public static final String AWS_REGION_ENV = "AWS_REGION"; 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"; diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java index bfacd5204..393835d1e 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java @@ -19,20 +19,24 @@ 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 @@ -40,7 +44,8 @@ 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) { @@ -79,20 +84,20 @@ 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; + return isColdStart == null; } public static void coldStartDone() { - IS_COLD_START = false; + isColdStart = false; } public static boolean isSamLocal() { @@ -100,14 +105,20 @@ public static boolean isSamLocal() { } public static Optional<String> getXrayTraceId() { - String X_AMZN_TRACE_ID = getenv(LambdaConstants.X_AMZN_TRACE_ID); + // 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 (X_AMZN_TRACE_ID == null) { - X_AMZN_TRACE_ID = getProperty(LambdaConstants.XRAY_TRACE_HEADER); + if (traceId == null) { + traceId = getProperty(LambdaConstants.XRAY_TRACE_HEADER); } - if (X_AMZN_TRACE_ID != null) { - return of(X_AMZN_TRACE_ID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")); + if (traceId != null) { + return of(traceId.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")); } return empty(); } diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index 17732cdf0..5c6bdc020 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -25,18 +25,26 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; 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 { + @AfterEach + void cleanup() { + SdkInternalThreadLocal.clear(); + } + @Test void isHandlerMethod_shouldRecognizeRequestHandler() { Context context = new TestLambdaContext(); @@ -159,6 +167,24 @@ void getXrayTraceId_notPresent() { 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(), new TestLambdaContext() }; 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 index 6cbbee583..43084dad2 100644 --- 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 @@ -1077,7 +1077,7 @@ }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", - "fields":[{"name":"IS_COLD_START"}] + "fields":[{"name":"isColdStart"}] }, { "name":"software.amazon.lambda.powertools.logging.log4j.BufferingAppender", 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 index 7ea81d690..b279d7d93 100644 --- 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 @@ -45,7 +45,7 @@ class PowerToolsResolverFactoryTest { @BeforeEach void setUp() throws IllegalAccessException, IOException { MDC.clear(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); context = new TestLambdaContext(); // Make sure file is cleaned up before running tests try { 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 index d4f4a4d7d..5c5eb83fa 100644 --- 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 @@ -163,7 +163,7 @@ }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", - "fields":[{"name":"IS_COLD_START"}] + "fields":[{"name":"isColdStart"}] }, { "name":"software.amazon.lambda.powertools.logging.logback.BufferingAppender", 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 index 65277e3d6..db3248b56 100644 --- 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 @@ -51,7 +51,7 @@ class LambdaEcsEncoderTest { @BeforeEach void setUp() throws IllegalAccessException, IOException { MDC.clear(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); context = new TestLambdaContext(); // Make sure file is cleaned up before running tests try { 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 index 912e2fde9..9ea275627 100644 --- 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 @@ -73,7 +73,7 @@ class LambdaJsonEncoderTest { @BeforeEach void setUp() throws IllegalAccessException, IOException { MDC.clear(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); context = new TestLambdaContext(); // Make sure file is cleaned up before running tests try { 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 index 4c66ebd97..9e665e87a 100644 --- 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 @@ -229,7 +229,7 @@ }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", - "fields":[{"name":"IS_COLD_START"}, {"name":"SERVICE_NAME"}] + "fields":[{"name":"isColdStart"}, {"name":"serviceName"}] }, { "name":"software.amazon.lambda.powertools.logging.argument.StructuredArgumentsTest", 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 3ff531321..c9baab1d8 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 @@ -100,7 +100,7 @@ class LambdaLoggingAspectTest { @BeforeEach void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException { MDC.clear(); - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); context = new TestLambdaContext(); requestHandler = new PowertoolsLogEnabled(); requestStreamHandler = new PowertoolsLogEnabledForStream(); @@ -418,7 +418,7 @@ void shouldHaveNoEffectIfNotUsedOnLambdaHandler() { @Test void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { // GIVEN - writeStaticField(LambdaHandlerProcessor.class, "SERVICE_NAME", "testService", true); + writeStaticField(LambdaHandlerProcessor.class, "serviceName", "testService", true); // WHEN requestHandler.handleRequest(new Object(), context); 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 index 533335782..a0ac5bfec 100644 --- 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 @@ -126,7 +126,7 @@ }, { "name": "software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", - "fields": [{ "name": "IS_COLD_START" }], + "fields": [{ "name": "isColdStart" }], "methods": [{ "name": "resetServiceName", "parameterTypes": [] }] }, { 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 index ecc7c13a1..c9c772313 100644 --- 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 @@ -52,13 +52,13 @@ class ConfigurationPrecedenceTest { void setUp() throws Exception { System.setOut(new PrintStream(outputStreamCaptor)); - // Reset LambdaHandlerProcessor's SERVICE_NAME + // Reset LambdaHandlerProcessor's serviceName Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); resetServiceName.setAccessible(true); resetServiceName.invoke(null); - // Reset IS_COLD_START - java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); coldStartField.setAccessible(true); coldStartField.set(null, null); } 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 index 962f2c2d7..4fc98d2a5 100644 --- 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 @@ -48,13 +48,13 @@ class MetricsFactoryTest { void setUp() throws Exception { System.setOut(new PrintStream(outputStreamCaptor)); - // Reset LambdaHandlerProcessor's SERVICE_NAME + // Reset LambdaHandlerProcessor's serviceName Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); resetServiceName.setAccessible(true); resetServiceName.invoke(null); - // Reset IS_COLD_START - java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); coldStartField.setAccessible(true); coldStartField.set(null, null); } 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 index d94a6bbe8..d3ed64fe3 100644 --- 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 @@ -40,13 +40,13 @@ void setUp() throws Exception { outputStreamCaptor = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStreamCaptor)); - // Reset LambdaHandlerProcessor's SERVICE_NAME + // Reset LambdaHandlerProcessor's serviceName Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); resetServiceName.setAccessible(true); resetServiceName.invoke(null); - // Reset IS_COLD_START - java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); coldStartField.setAccessible(true); coldStartField.set(null, null); } 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 index 9f793f977..bab039640 100644 --- 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 @@ -56,13 +56,13 @@ class EmfMetricsLoggerTest { @BeforeEach void setUp() throws Exception { - // Reset LambdaHandlerProcessor's SERVICE_NAME + // Reset LambdaHandlerProcessor's serviceName Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); resetServiceName.setAccessible(true); resetServiceName.invoke(null); - // Reset IS_COLD_START - java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); coldStartField.setAccessible(true); coldStartField.set(null, null); 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 326dd2ffe..031fe4553 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 @@ -49,13 +49,13 @@ class LambdaMetricsAspectTest { void setUp() throws Exception { System.setOut(new PrintStream(outputStreamCaptor)); - // Reset LambdaHandlerProcessor's SERVICE_NAME + // Reset LambdaHandlerProcessor's serviceName Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName"); resetServiceName.setAccessible(true); resetServiceName.invoke(null); - // Reset IS_COLD_START - java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START"); + // Reset isColdStart + java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart"); coldStartField.setAccessible(true); coldStartField.set(null, null); } 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 index a71154276..97e0a7a86 100644 --- 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 @@ -302,7 +302,7 @@ }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", - "fields":[{"name":"IS_COLD_START"}] + "fields":[{"name":"isColdStart"}] }, { "name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabled", 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 4d2e481b1..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 @@ -59,7 +59,7 @@ class LambdaTracingAspectTest { @BeforeEach void setUp() throws IllegalAccessException { - writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true); context = new TestLambdaContext(); requestHandler = new PowerTracerToolEnabled(); streamHandler = new PowerTracerToolEnabledForStream(); From bfc4f8c6510537ec37c810c46875157570d8a73d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:48:11 +0200 Subject: [PATCH 0888/1008] chore: bump aws.xray.recorder.version from 2.19.0 to 2.20.0 (#2185) Bumps `aws.xray.recorder.version` from 2.19.0 to 2.20.0. Updates `com.amazonaws:aws-xray-recorder-sdk-core` from 2.19.0 to 2.20.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.19.0...v2.20.0) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core` from 2.19.0 to 2.20.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.19.0...v2.20.0) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2` from 2.19.0 to 2.20.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.19.0...v2.20.0) Updates `com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` from 2.19.0 to 2.20.0 - [Release notes](https://github.com/aws/aws-xray-sdk-java/releases) - [Changelog](https://github.com/aws/aws-xray-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-xray-sdk-java/compare/v2.19.0...v2.20.0) --- updated-dependencies: - dependency-name: com.amazonaws:aws-xray-recorder-sdk-core dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2 dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor dependency-version: 2.20.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Philipp Page <github@philipp.page> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a04780ba2..c1d0720b8 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> <aws.sdk.version>2.35.6</aws.sdk.version> - <aws.xray.recorder.version>2.19.0</aws.xray.recorder.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.4.0</lambda.core.version> From 39a7c2ed81abef679a92f697ca6e226b46d76a0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:33:07 +0200 Subject: [PATCH 0889/1008] chore: bump io.github.ascopes:protobuf-maven-plugin (#2193) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.10.0 to 3.10.2. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.10.0...v3.10.2) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.10.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 8a0ac2d89..6527bf278 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.10.0</version> + <version>3.10.2</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index b1f1afd3e..2985b3f0d 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -196,7 +196,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.10.0</version> + <version>3.10.2</version> <executions> <execution> <id>generate-test-sources</id> From ac3586c51c4907478126ea36a80ea8f429298a21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:44:21 +0200 Subject: [PATCH 0890/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.214.0 to 2.220.0 (#2191) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.214.0 to 2.220.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.214.0...v2.220.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.220.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 491c9ec68..800d896d0 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.4.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.214.0</cdk.version> + <cdk.version>2.220.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.13.4</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 32ea35d36..20d28895e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.214.0</cdk.version> + <cdk.version>2.220.0</cdk.version> </properties> <dependencies> From 2eb25a3d1cea8ae1af7ba590b0924a5f652a363e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:45:02 +0200 Subject: [PATCH 0891/1008] chore: bump com.github.spotbugs:spotbugs-maven-plugin (#2192) Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.4.2 to 4.9.7.0. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.4.2...spotbugs-maven-plugin-4.9.7.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.7.0 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1d0720b8..4e805e5ae 100644 --- a/pom.xml +++ b/pom.xml @@ -598,7 +598,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.9.4.2</version> + <version>4.9.7.0</version> <executions> <execution> <id>test</id> From 9cce3da3225c846b680733780652291a313b5641 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:45:18 +0200 Subject: [PATCH 0892/1008] chore: bump squidfunk/mkdocs-material in /docs (#2194) Bumps squidfunk/mkdocs-material from `00f9276` to `f5c556a`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: f5c556a6d30ce0c1c0df10e3c38c79bbcafdaea4b1c1be366809d0d4f6f9d57f dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index fe4f2ec55..9b8bde4c5 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:00f9276315990b82f5af8c47bb2b71e2c69aef9f02a08f8dffd2515f42310753 +FROM squidfunk/mkdocs-material@sha256:f5c556a6d30ce0c1c0df10e3c38c79bbcafdaea4b1c1be366809d0d4f6f9d57f COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 03a5af5b456bb9fdc274f2872b7d76c6f10c10f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:45:31 +0200 Subject: [PATCH 0893/1008] chore: bump sam/build-java21 (#2195) Bumps sam/build-java21 from `fd3b445` to `1d40149`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 1d40149444f21a5b20e8f54275f9173265d57522ff8669fa3f8fefbaf24657da dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 5086f4532..e82e5d33e 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:fd3b44561b51626c15ae4437630afcfdc84ad883edca64d2cdb87aad755eb7ac +FROM public.ecr.aws/sam/build-java21@sha256:1d40149444f21a5b20e8f54275f9173265d57522ff8669fa3f8fefbaf24657da # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From a4b1e12877510a44be9f821a7550959928867c69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:29:23 +0100 Subject: [PATCH 0894/1008] chore: bump com.networknt:json-schema-validator from 1.5.8 to 1.5.9 (#2189) Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.5.8 to 1.5.9. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.8...1.5.9) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-version: 1.5.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-validation/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 6ecee16a1..4194b4240 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -65,7 +65,7 @@ <dependency> <groupId>com.networknt</groupId> <artifactId>json-schema-validator</artifactId> - <version>1.5.8</version> + <version>1.5.9</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> From a91c09099d838cd6f4f4cc1e426a3c835f13f1c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:29:57 +0100 Subject: [PATCH 0895/1008] chore: bump aws.sdk.version from 2.35.6 to 2.35.7 (#2190) Bumps `aws.sdk.version` from 2.35.6 to 2.35.7. Updates `software.amazon.awssdk:bom` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:http-client-spi` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.35.7 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.35.7 Updates `software.amazon.awssdk:dynamodb` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:lambda` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.35.7 Updates `software.amazon.awssdk:cloudwatch` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:xray` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.35.7 Updates `software.amazon.awssdk:cloudformation` from 2.35.6 to 2.35.7 Updates `software.amazon.awssdk:sts` from 2.35.6 to 2.35.7 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.35.7 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.35.7 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.35.7 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.35.7 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c5245e89f..bc3084990 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.35.6</aws.sdk.version> + <aws.sdk.version>2.35.7</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 4e805e5ae..0feecd82b 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.35.6</aws.sdk.version> + <aws.sdk.version>2.35.7</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 69c55842c..f7c78c131 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.35.6</aws.sdk.version> + <aws.sdk.version>2.35.7</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From b923cd0b2c3a9dc601d3b3999b30df5eb962d362 Mon Sep 17 00:00:00 2001 From: kjswaruph <jyothiswaruphkolli@gmail.com> Date: Thu, 16 Oct 2025 15:31:09 +0530 Subject: [PATCH 0896/1008] feat: add CRaC priming support to powertools-kafka module (#2145) * feat: add CRaC priming support to powertools-kafka module - Add maven test profile and classesloaded.txt for preloading - Add Crac dependency and update PowertoolsSerializer to register as Crac Resource - Add tests in PowertoolsSerializerTest to assert beforeCheckpoint and afterRestore hooks do not throw exception * feat: add avro priming * refactor: remove avro priming * fix: add space before opening braces Co-authored-by: Philipp Page <philipp.page@yahoo.de> * Add powertools-kafka to check build workflows. * Make input streams auto-closable in PowertoolsSerializerTest * Wrap result input stream directly in try clause. * Fix PMD finding for IOException input stream. --------- Co-authored-by: Philipp Page <philipp.page@yahoo.de> Co-authored-by: Philipp Page <pagejep@amazon.com> --- .github/workflows/check-build.yml | 1 + .github/workflows/check-spotbugs.yml | 3 +- powertools-kafka/pom.xml | 25 + .../kafka/PowertoolsSerializer.java | 79 +- .../src/main/resources/classesloaded.txt | 6642 +++++++++++++++++ .../kafka/PowertoolsSerializerTest.java | 95 +- 6 files changed, 6806 insertions(+), 39 deletions(-) create mode 100644 powertools-kafka/src/main/resources/classesloaded.txt diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 0167925f4..8bc474c20 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -23,6 +23,7 @@ on: - 'powertools-large-messages/**' - 'powertools-logging/**' - 'powertools-metrics/**' + - 'powertools-kafka/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - 'powertools-sqs/**' diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index 214125ed4..bf746320d 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -20,6 +20,7 @@ on: - 'powertools-large-messages/**' - 'powertools-logging/**' - 'powertools-metrics/**' + - 'powertools-kafka/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - 'powertools-sqs/**' @@ -47,4 +48,4 @@ jobs: 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 \ No newline at end of file + run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=true diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 2985b3f0d..3dae1ffd2 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -88,6 +88,10 @@ <artifactId>sdk-core</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.crac</groupId> + <artifactId>crac</artifactId> + </dependency> <!-- Test dependencies --> <dependency> @@ -238,4 +242,25 @@ </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/PowertoolsSerializer.java b/powertools-kafka/src/main/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializer.java index be8563b8e..619e46729 100644 --- 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 @@ -14,12 +14,20 @@ 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; @@ -30,11 +38,11 @@ /** * 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 { +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(), @@ -43,6 +51,13 @@ DeserializationType.KAFKA_PROTOBUF, new KafkaProtobufDeserializer(), 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(), @@ -64,4 +79,64 @@ 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/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/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java b/powertools-kafka/src/test/java/software/amazon/lambda/powertools/kafka/PowertoolsSerializerTest.java index 6ce57ecd5..d7d045877 100644 --- 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 @@ -13,7 +13,9 @@ 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; @@ -30,6 +32,8 @@ 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; @@ -62,7 +66,7 @@ static Stream<InputType> inputTypes() { @ParameterizedTest @MethodSource("inputTypes") @SetEnvironmentVariable(key = "_HANDLER", value = "") - void shouldUseDefaultDeserializerWhenHandlerNotFound(InputType inputType) throws JsonProcessingException { + void shouldUseDefaultDeserializerWhenHandlerNotFound(InputType inputType) throws JsonProcessingException, IOException { // When PowertoolsSerializer serializer = new PowertoolsSerializer(); @@ -73,8 +77,9 @@ void shouldUseDefaultDeserializerWhenHandlerNotFound(InputType inputType) throws // This will use the Lambda default deserializer (no Kafka logic) TestProductPojo result; if (inputType == InputType.INPUT_STREAM) { - ByteArrayInputStream input = new ByteArrayInputStream(json.getBytes()); - result = serializer.fromJson(input, TestProductPojo.class); + try (ByteArrayInputStream input = new ByteArrayInputStream(json.getBytes())) { + result = serializer.fromJson(input, TestProductPojo.class); + } } else { result = serializer.fromJson(json, TestProductPojo.class); } @@ -88,7 +93,7 @@ void shouldUseDefaultDeserializerWhenHandlerNotFound(InputType inputType) throws @ParameterizedTest @MethodSource("inputTypes") @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.DefaultHandler::handleRequest") - void shouldUseLambdaDefaultDeserializer(InputType inputType) throws JsonProcessingException { + void shouldUseLambdaDefaultDeserializer(InputType inputType) throws JsonProcessingException, IOException { // When PowertoolsSerializer serializer = new PowertoolsSerializer(); @@ -99,8 +104,9 @@ void shouldUseLambdaDefaultDeserializer(InputType inputType) throws JsonProcessi // This will use the Lambda default deserializer (no Kafka logic) TestProductPojo result; if (inputType == InputType.INPUT_STREAM) { - ByteArrayInputStream input = new ByteArrayInputStream(json.getBytes()); - result = serializer.fromJson(input, TestProductPojo.class); + try (ByteArrayInputStream input = new ByteArrayInputStream(json.getBytes())) { + result = serializer.fromJson(input, TestProductPojo.class); + } } else { result = serializer.fromJson(json, TestProductPojo.class); } @@ -134,30 +140,28 @@ void shouldHandleInputStreamType() throws IOException { // Then String testInput = "This is a test string"; - ByteArrayInputStream inputStream = new ByteArrayInputStream(testInput.getBytes()); - - // This should return the input stream directly - 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); + 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() { + void shouldConvertInputStreamToString() throws IOException { // When LambdaDefaultDeserializer deserializer = new LambdaDefaultDeserializer(); // Then String expected = "This is a test string"; - ByteArrayInputStream inputStream = new ByteArrayInputStream(expected.getBytes()); - - // Convert InputStream to String - String result = deserializer.fromJson(inputStream, String.class); + 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); + // Verify the result + assertThat(result).isEqualTo(expected); + } } @Test @@ -166,7 +170,7 @@ void shouldThrowRuntimeExceptionWhenInputStreamIsInvalid() { LambdaDefaultDeserializer deserializer = new LambdaDefaultDeserializer(); // Create a problematic InputStream that throws IOException when read - InputStream problematicStream = new InputStream() { + try (InputStream problematicStream = new InputStream() { @Override public int read() throws IOException { throw new IOException("Simulated IO error"); @@ -176,12 +180,14 @@ public int read() throws IOException { 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"); + }) { + // 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 @@ -203,7 +209,7 @@ void shouldConvertStringToByteArray() { @ParameterizedTest @MethodSource("inputTypes") @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.JsonHandler::handleRequest") - void shouldUseKafkaJsonDeserializer(InputType inputType) throws JsonProcessingException { + void shouldUseKafkaJsonDeserializer(InputType inputType) throws IOException { // When PowertoolsSerializer serializer = new PowertoolsSerializer(); @@ -237,8 +243,9 @@ void shouldUseKafkaJsonDeserializer(InputType inputType) throws JsonProcessingEx ConsumerRecords<String, TestProductPojo> records; if (inputType == InputType.INPUT_STREAM) { - ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes()); - records = serializer.fromJson(input, type); + try (ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes())) { + records = serializer.fromJson(input, type); + } } else { records = serializer.fromJson(kafkaJson, type); } @@ -298,8 +305,9 @@ void shouldUseKafkaAvroDeserializer(InputType inputType) throws IOException { ConsumerRecords<String, software.amazon.lambda.powertools.kafka.serializers.test.avro.TestProduct> records; if (inputType == InputType.INPUT_STREAM) { - ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes()); - records = serializer.fromJson(input, type); + try (ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes())) { + records = serializer.fromJson(input, type); + } } else { records = serializer.fromJson(kafkaJson, type); } @@ -326,7 +334,7 @@ void shouldUseKafkaAvroDeserializer(InputType inputType) throws IOException { @ParameterizedTest @MethodSource("inputTypes") @SetEnvironmentVariable(key = "_HANDLER", value = "software.amazon.lambda.powertools.kafka.testutils.ProtobufHandler::handleRequest") - void shouldUseKafkaProtobufDeserializer(InputType inputType) { + void shouldUseKafkaProtobufDeserializer(InputType inputType) throws IOException { // When PowertoolsSerializer serializer = new PowertoolsSerializer(); @@ -365,8 +373,9 @@ void shouldUseKafkaProtobufDeserializer(InputType inputType) { ConsumerRecords<String, software.amazon.lambda.powertools.kafka.serializers.test.protobuf.TestProduct> records; if (inputType == InputType.INPUT_STREAM) { - ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes()); - records = serializer.fromJson(input, type); + try (ByteArrayInputStream input = new ByteArrayInputStream(kafkaJson.getBytes())) { + records = serializer.fromJson(input, type); + } } else { records = serializer.fromJson(kafkaJson, type); } @@ -414,4 +423,18 @@ void shouldDelegateToJsonOutput() { 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)); + } } From fab11efc8828fd49366c96df8d94a7574bed4b04 Mon Sep 17 00:00:00 2001 From: Connor Kirkpatrick <17845406+ConnorKirk@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:11:31 +0100 Subject: [PATCH 0897/1008] chore: update contributing.md (#2203) --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4330cf8e..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. From 0fa97ec29d0f2ec46e9462cb84aff608e24e9c03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:20:54 +0200 Subject: [PATCH 0898/1008] chore: bump com.google.protobuf:protobuf-java from 4.32.1 to 4.33.0 (#2198) Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 4.32.1 to 4.33.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-version: 4.33.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 6527bf278..d7778c896 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -12,7 +12,7 @@ <maven.compiler.target>11</maven.compiler.target> <aspectj.version>1.9.20.1</aspectj.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.32.1</protobuf.version> + <protobuf.version>4.33.0</protobuf.version> </properties> <dependencies> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 3dae1ffd2..a6a090b5d 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -36,7 +36,7 @@ <properties> <kafka-clients.version>4.1.0</kafka-clients.version> <avro.version>1.12.0</avro.version> - <protobuf.version>4.32.1</protobuf.version> + <protobuf.version>4.33.0</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> From eeea68dc9ca6c179b843f9a1281b89615d663c58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:21:12 +0200 Subject: [PATCH 0899/1008] chore: bump aws.sdk.version from 2.35.7 to 2.35.8 (#2197) Bumps `aws.sdk.version` from 2.35.7 to 2.35.8. Updates `software.amazon.awssdk:bom` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:http-client-spi` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.35.8 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.35.8 Updates `software.amazon.awssdk:dynamodb` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:lambda` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.35.8 Updates `software.amazon.awssdk:cloudwatch` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:xray` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.35.8 Updates `software.amazon.awssdk:cloudformation` from 2.35.7 to 2.35.8 Updates `software.amazon.awssdk:sts` from 2.35.7 to 2.35.8 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.35.8 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.35.8 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.35.8 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.35.8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index bc3084990..39f09da04 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.35.7</aws.sdk.version> + <aws.sdk.version>2.35.8</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 0feecd82b..e77ca1620 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.35.7</aws.sdk.version> + <aws.sdk.version>2.35.8</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index f7c78c131..b67207e75 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.35.7</aws.sdk.version> + <aws.sdk.version>2.35.8</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From faa915b8124247aba600c111c17738a8ad7daff4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:21:30 +0200 Subject: [PATCH 0900/1008] chore: bump org.sonatype.central:central-publishing-maven-plugin (#2196) Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.8.0 to 0.9.0. - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) --- updated-dependencies: - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e77ca1620..8bed88cd0 100644 --- a/pom.xml +++ b/pom.xml @@ -524,7 +524,7 @@ <plugin> <groupId>org.sonatype.central</groupId> <artifactId>central-publishing-maven-plugin</artifactId> - <version>0.8.0</version> + <version>0.9.0</version> <extensions>true</extensions> <configuration> <publishingServerId>central</publishingServerId> From 291f2b48778a93b2a850535309d169abb082a0ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:21:54 +0200 Subject: [PATCH 0901/1008] chore: bump sam/build-java21 (#2201) Bumps sam/build-java21 from `1d40149` to `e12c501`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: e12c501aa7d3d3dc04be0d900115a0339c73f523ad73a9aecd9e99499413c465 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index e82e5d33e..7dc04ec90 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:1d40149444f21a5b20e8f54275f9173265d57522ff8669fa3f8fefbaf24657da +FROM public.ecr.aws/sam/build-java21@sha256:e12c501aa7d3d3dc04be0d900115a0339c73f523ad73a9aecd9e99499413c465 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From d0cdebd659d7cf0cff8159c6ffb0d79c277cf451 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:34:25 +0200 Subject: [PATCH 0902/1008] chore: bump github/codeql-action from 4.30.8 to 4.30.9 (#2209) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.8 to 4.30.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f443b600d91635bebf5b0d9ebc620189c0d6fba5...16140ae1a102900babc80a33c44059580f687047) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.30.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index bd9f16d74..d4df1e675 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v3.29.5 + uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v3.29.5 with: sarif_file: results.sarif From dd7a49c25a6193a729a68dcb03f9d3f33af44920 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 21 Oct 2025 11:13:34 +0200 Subject: [PATCH 0903/1008] Fix flaky unit tests. (#2211) --- .../json/resolver/PowerToolsResolverFactoryTest.java | 8 ++++++-- .../json/resolver/PowertoolsResolverArgumentsTest.java | 10 +++++++--- .../logging/log4j/BufferingAppenderTest.java | 6 +++++- .../logging/internal/LambdaEcsEncoderTest.java | 6 +++++- .../logging/internal/LambdaJsonEncoderTest.java | 6 +++++- .../powertools/logging/internal/KeyBufferTest.java | 9 +++++++-- .../logging/internal/LambdaLoggingAspectTest.java | 6 +++++- 7 files changed, 40 insertions(+), 11 deletions(-) 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 index b279d7d93..7eeffc78f 100644 --- 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 @@ -58,8 +58,12 @@ void setUp() throws IllegalAccessException, IOException { @AfterEach void cleanUp() throws IOException { - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + 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 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 index 5432e45ae..546d54579 100644 --- 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 @@ -59,9 +59,13 @@ void setUp() throws IOException { @AfterEach void cleanUp() throws IOException { - // 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(); + 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 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 index 434d3983b..a0fac8d7a 100644 --- 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 @@ -36,7 +36,11 @@ void setUp() throws IOException { @AfterEach void cleanUp() throws IOException { - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + 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 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 index db3248b56..898abec2e 100644 --- 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 @@ -63,7 +63,11 @@ void setUp() throws IllegalAccessException, IOException { @AfterEach void cleanUp() throws IOException { - FileChannel.open(Paths.get("target/ecslogfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + 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 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 index 9ea275627..326fa4b19 100644 --- 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 @@ -85,7 +85,11 @@ void setUp() throws IllegalAccessException, IOException { @AfterEach void cleanUp() throws IOException { - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + 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 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 index 15a54fa5c..fac85e230 100644 --- 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 @@ -21,6 +21,7 @@ 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; @@ -44,7 +45,7 @@ void setUp() throws IOException { // Clean up log file before each test try { FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); - } catch (IOException e) { + } catch (NoSuchFileException e) { // may not be there in the first run } } @@ -52,7 +53,11 @@ void setUp() throws IOException { @AfterEach void cleanUp() throws IOException { // Make sure file is cleaned up after each test - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + 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 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 c9baab1d8..f77997a77 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 @@ -126,7 +126,11 @@ void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTar @AfterEach void cleanUp() throws IOException { // Make sure file is cleaned up before running full stack logging regression - FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + 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 From 87851cec37a42e6edb68510f7dc86114642b98ca Mon Sep 17 00:00:00 2001 From: Simon Thulbourn <sthulb@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:26:58 +0200 Subject: [PATCH 0904/1008] chore(ci): fix changelog generation (#2207) * chore(ci): fix changelog generation * update change log on release * update to use sha256 --------- Co-authored-by: Philipp Page <github@philipp.page> --- .chglog/CHANGELOG.tpl.md | 67 ++ .chglog/config.yml | 37 + .github/workflows/release.yml | 3 +- CHANGELOG.md | 1293 ++++++++++++++++++++++++++------- 4 files changed, 1121 insertions(+), 279 deletions(-) create mode 100644 .chglog/CHANGELOG.tpl.md create mode 100644 .chglog/config.yml 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 @@ +<!-- changelog is partially generated, so it doesn't follow headings and required structure, so we disable it. --> +<!-- markdownlint-disable --> + +{{ if .Versions -}} +<a name="unreleased"></a> +# 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 }} +<a name="{{ .Tag.Name }}"></a> +## {{ 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/workflows/release.yml b/.github/workflows/release.yml index d1f094f86..c6a3e415b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -240,9 +240,10 @@ jobs: git config pull.rebase true git config remote.origin.url >&- - id: branch - name: Create 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 20a04c488..a07df5d3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,378 +1,1115 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -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). - - - -## [Unreleased] - -## [1.20.1] - 2025-04-08 - -* docs: fix 2 typos (#1739) by @ntestor -* docs: Correct XML formatting for Maven configuration in Large Messages utility docs (#1796) by @jreijn -* fix: Load version.properties file as resource stream to fix loading when packaged as jar (#1813) by @phipag - -## [1.20.0] - 2025-03-25 - -* feat(cfn-custom-resource): Add optional 'reason' field for detailed failure reporting (#1758) by @moizsh - -## [1.19.0] - 2025-03-07 - -* chore(deps): Update deps for jackson ([#1793](https://github.com/aws-powertools/powertools-lambda-java/pull/1793)) by [@sthulb](https://github.com/sthulb) -* build(deps): bump log4j.version from 2.22.1 to 2.24.3 ([#1777](https://github.com/aws-powertools/powertools-lambda-java/pull/1777)) by [@dependabot](https://github.com/dependabot) -* chore(deps): update JSII to 1.108 ([#1791](https://github.com/aws-powertools/powertools-lambda-java/pull/1791)) by [@sthulb](https://github.com/sthulb) -* build(deps): bump jinja2 from 3.1.5 to 3.1.6 in /docs ([#1789](https://github.com/aws-powertools/powertools-lambda-java/pull/1789)) by [@dependabot](https://github.com/dependabot) -* chore: Update netty version ([#1768](https://github.com/aws-powertools/powertools-lambda-java/pull/1768)) by [@sthulb](https://github.com/sthulb) -* chore: Set versions of transitive dependencies ([#1767](https://github.com/aws-powertools/powertools-lambda-java/pull/1767)) by [@sthulb](https://github.com/sthulb) -* chore: update Jackson in examples ([#1766](https://github.com/aws-powertools/powertools-lambda-java/pull/1766)) by [@sthulb](https://github.com/sthulb) -* build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.4.1 to 3.4.2 ([#1731](https://github.com/aws-powertools/powertools-lambda-java/pull/1731)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.xray.recorder.version from 2.15.3 to 2.18.1 ([#1726](https://github.com/aws-powertools/powertools-lambda-java/pull/1726)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.26.29 to 2.27.12 ([#1724](https://github.com/aws-powertools/powertools-lambda-java/pull/1724)) by [@dependabot](https://github.com/dependabot) -* fix: Allow empty responses as well as null response in AppConfig ([#1673](https://github.com/aws-powertools/powertools-lambda-java/pull/1673)) by [@chrisclayson](https://github.com/chrisclayson) -* build(deps): bump aws.sdk.version from 2.27.2 to 2.27.7 ([#1715](https://github.com/aws-powertools/powertools-lambda-java/pull/1715)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.26.29 to 2.27.2 ([#1714](https://github.com/aws-powertools/powertools-lambda-java/pull/1714)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.26 to 2.26.29 ([#1713](https://github.com/aws-powertools/powertools-lambda-java/pull/1713)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.26.25 to 2.26.29 ([#1712](https://github.com/aws-powertools/powertools-lambda-java/pull/1712)) by [@dependabot](https://github.com/dependabot) -* chore: deprecate java1.8 al1 ([#1706](https://github.com/aws-powertools/powertools-lambda-java/pull/1706)) by [@jeromevdl](https://github.com/jeromevdl) -* chore: java 1.8 AL1 is deprecated, fix E2E tests ([#1692](https://github.com/aws-powertools/powertools-lambda-java/pull/1692)) by [@jeromevdl](https://github.com/jeromevdl) -* build(deps): bump aws.sdk.version from 2.26.21 to 2.26.25 ([#1703](https://github.com/aws-powertools/powertools-lambda-java/pull/1703)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.26.3 to 2.26.21 ([#1697](https://github.com/aws-powertools/powertools-lambda-java/pull/1697)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump jackson.version from 2.17.0 to 2.17.2 ([#1696](https://github.com/aws-powertools/powertools-lambda-java/pull/1696)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.commons:commons-lang3 from 3.13.0 to 3.14.0 ([#1694](https://github.com/aws-powertools/powertools-lambda-java/pull/1694)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump commons-io:commons-io from 2.15.1 to 2.16.1 ([#1691](https://github.com/aws-powertools/powertools-lambda-java/pull/1691)) by [@dependabot](https://github.com/dependabot) -* docs: improve tracing doc for sdk instrumentation ([#1687](https://github.com/aws-powertools/powertools-lambda-java/pull/1687)) by [@jeromevdl](https://github.com/jeromevdl) -* docs: fix tracing links for xray ([#1686](https://github.com/aws-powertools/powertools-lambda-java/pull/1686)) by [@jeromevdl](https://github.com/jeromevdl) -* build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.5 to 3.3.0 ([#1679](https://github.com/aws-powertools/powertools-lambda-java/pull/1679)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.69 to 2.26.3 ([#1658](https://github.com/aws-powertools/powertools-lambda-java/pull/1658)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump com.github.spotbugs:spotbugs-maven-plugin from 4.7.3.6 to 4.8.5.0 ([#1657](https://github.com/aws-powertools/powertools-lambda-java/pull/1657)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.3.0 to 3.4.0 ([#1653](https://github.com/aws-powertools/powertools-lambda-java/pull/1653)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.50 to 2.25.69 ([#1652](https://github.com/aws-powertools/powertools-lambda-java/pull/1652)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1 ([#1646](https://github.com/aws-powertools/powertools-lambda-java/pull/1646)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.assertj:assertj-core from 3.25.3 to 3.26.0 ([#1644](https://github.com/aws-powertools/powertools-lambda-java/pull/1644)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.xray.recorder.version from 2.15.1 to 2.15.3 ([#1643](https://github.com/aws-powertools/powertools-lambda-java/pull/1643)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.35 to 2.25.50 ([#1642](https://github.com/aws-powertools/powertools-lambda-java/pull/1642)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump com.amazonaws:aws-lambda-java-events from 3.11.2 to 3.11.4 ([#1597](https://github.com/aws-powertools/powertools-lambda-java/pull/1597)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.24.10 to 2.25.6 ([#1603](https://github.com/aws-powertools/powertools-lambda-java/pull/1603)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.5 ([#1596](https://github.com/aws-powertools/powertools-lambda-java/pull/1596)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.2.0 ([#1585](https://github.com/aws-powertools/powertools-lambda-java/pull/1585)) by [@dependabot](https://github.com/dependabot) -* build(deps-dev): bump software.amazon.awscdk:aws-cdk-lib from 2.100.0 to 2.130.0 ([#1586](https://github.com/aws-powertools/powertools-lambda-java/pull/1586)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump io.burt:jmespath-jackson from 0.5.1 to 0.6.0 ([#1587](https://github.com/aws-powertools/powertools-lambda-java/pull/1587)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.21.0 to 2.24.10 ([#1581](https://github.com/aws-powertools/powertools-lambda-java/pull/1581)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump commons-io:commons-io from 2.13.0 to 2.15.1 ([#1584](https://github.com/aws-powertools/powertools-lambda-java/pull/1584)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.xray.recorder.version from 2.14.0 to 2.15.1 ([#1583](https://github.com/aws-powertools/powertools-lambda-java/pull/1583)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-shade-plugin from 3.5.0 to 3.5.2 ([#1582](https://github.com/aws-powertools/powertools-lambda-java/pull/1582)) by [@dependabot](https://github.com/dependabot) -* build(deps-dev): bump org.yaml:snakeyaml from 2.1 to 2.2 ([#1400](https://github.com/aws-powertools/powertools-lambda-java/pull/1400)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump log4j.version from 2.20.0 to 2.22.1 ([#1547](https://github.com/aws-powertools/powertools-lambda-java/pull/1547)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-artifact-plugin from 3.4.1 to 3.5.0 ([#1485](https://github.com/aws-powertools/powertools-lambda-java/pull/1485)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump com.amazonaws:aws-lambda-java-serialization from 1.1.2 to 1.1.5 ([#1573](https://github.com/aws-powertools/powertools-lambda-java/pull/1573)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.10 to 0.8.11 ([#1509](https://github.com/aws-powertools/powertools-lambda-java/pull/1509)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aspectj to 1.9.21 for jdk21 ([#1536](https://github.com/aws-powertools/powertools-lambda-java/pull/1536)) by [@jeromevdl](https://github.com/jeromevdl) -* docs: HelloWorldStreamFunction in examples fails with sam ([#1532](https://github.com/aws-powertools/powertools-lambda-java/pull/1532)) by [@jasoniharris](https://github.com/jasoniharris) -* chore: Testing java21 aspectj pre-release ([#1519](https://github.com/aws-powertools/powertools-lambda-java/pull/1519)) by [@scottgerring](https://github.com/scottgerring) -* fix: LargeMessageIdempotentE2ET Flaky ([#1518](https://github.com/aws-powertools/powertools-lambda-java/pull/1518)) by [@scottgerring](https://github.com/scottgerring) -* build(deps): bump software.amazon.payloadoffloading:payloadoffloading-common from 2.1.3 to 2.2.0 ([#1639](https://github.com/aws-powertools/powertools-lambda-java/pull/1639)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.3.0 to 3.4.1 ([#1638](https://github.com/aws-powertools/powertools-lambda-java/pull/1638)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump jackson.version from 2.15.3 to 2.17.0 ([#1637](https://github.com/aws-powertools/powertools-lambda-java/pull/1637)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.31 to 2.25.35 ([#1629](https://github.com/aws-powertools/powertools-lambda-java/pull/1629)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.16 to 2.25.31 ([#1625](https://github.com/aws-powertools/powertools-lambda-java/pull/1625)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.21.1 to 2.25.26 ([#1622](https://github.com/aws-powertools/powertools-lambda-java/pull/1622)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-failsafe-plugin from 3.1.2 to 3.2.5 ([#1619](https://github.com/aws-powertools/powertools-lambda-java/pull/1619)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump com.fasterxml.jackson.datatype:jackson-datatype-joda from 2.15.2 to 2.17.0 ([#1616](https://github.com/aws-powertools/powertools-lambda-java/pull/1616)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump aws.sdk.version from 2.25.6 to 2.25.16 ([#1613](https://github.com/aws-powertools/powertools-lambda-java/pull/1613)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.1.0 to 3.2.1 ([#1610](https://github.com/aws-powertools/powertools-lambda-java/pull/1610)) by [@dependabot](https://github.com/dependabot) -* build(deps): bump org.assertj:assertj-core from 3.24.2 to 3.25.3 ([#1609](https://github.com/aws-powertools/powertools-lambda-java/pull/1609)) by [@dependabot](https://github.com/dependabot) - -## [1.18.0] - 2023-11-16 - -### Added - -* feat: add support for [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced) (#1514) by @jeromevdl -* feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) by @AlexeySoshin - -### Maintenance - -* fix: json schema 403 error (#1457) by @jeromevdl -* fix: array jmespath fail in idempotency module (#1420) by @jeromevdl -* chore: java21 support in our build (#1488) by @jeromevdl -* chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) by @jdoherty -* fix: null namespace should fallback to default namespace (#1506) by @jeromevdl -* fix: get trace id from system property when env var is not set (#1503) by @mriccia -* chore: artifacts size on good branches (#1493) by @jeromevdl -* fix: enforce jackson databind version (#1472) by @jeromevdl -* chore: add missing projects and improve workflow (#1487) by @jeromevdl -* chore: Reporting size of the jars in GitHub comments (#1196) by @jeromevdl -* Deps: Bump third party dependencies to the latest versions. - -### Documentation - -* docs(customer-reference): add Vertex Pharmaceuticals as a customer reference (#1486) by @scottgerring -* docs: Adding Kotlin example. (#1454) by @jasoniharris -* docs: Terraform example (#1478) by @skal111 -* docs: Add Serveless Framework example (#1363) by @AlexeySoshin -* docs: Fix link to SQS large message migration guide (#1422) by @scottgerring -* docs(logging): correct log example keys (#1411) by @walmsles -* docs: Update gradle configuration readme (#1359) by @scottgerring - -## [1.17.0] - 2023-08-21 - -### Added -* Feat: Add Batch Processor module in (#1317) by @scottgerring -* Feat: Add SNS+SQS large messages module (#1310) by @jeromevdl - -### Maintenance -* fix: use default credentials provider for all provided SDK clients in (#1303) by @roamingthings -* Chore: Make request for Logger explicitly for current class in (#1307) by @jreijn -* Chore: checkstyle formater & linter in (#1316) by @jeromevdl -* Chore: Add powertools specific user-agent-suffix to the AWS SDK v2 clients by @eldimi in (#1306) -* Chore: Add 'v2' branch to build workflows to prepare for v2 work in (#1341) by @scottgerring -* Deps: Bump third party dependencies to the latest versions. - -### Documentation -* Docs: Add maintainers guide in (#1326) by @scottgerring -* Docs: improve contributing guide in (#1334) by @jeromevdl -* Docs: Improve example documentation in (#1291) by @scottgerring -* Docs: Add discord + sec disclosure links to readme in (#1311) by @scottgerring -* Docs: Add external examples from AWS SAM CLI App Templates in (#1318) by @AlexeySoshin -* Docs: Add CDK example in (#1321) by @AlexeySoshin +<!-- 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)) -## [1.16.1] - 2023-07-19 +## Documentation -* 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. +* Announce deprecation of v1 +* Version documentation ([#1878](https://github.com/aws-powertools/powertools-lambda-java/issues/1878)) +## Features -## [1.16.0] - 2023-06-29 +* **kafka:** New Kafka utility ([#1898](https://github.com/aws-powertools/powertools-lambda-java/issues/1898)) +## Maintenance -### Added -* Feature: Add AppConfig provider to parameters module (#1104) by @scottgerring +* **ci:** Update workflows to make v2 the default ([#1888](https://github.com/aws-powertools/powertools-lambda-java/issues/1888)) -### 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. +<a name="v2.0.0"></a> +## [v2.0.0] - 2025-06-12 +## Maintenance -## [1.15.0] - 2023-03-20 -### 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 -### 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 +<a name="v2.0.0-RC1"></a> +## [v2.0.0-RC1] - 2025-06-11 +## Bug Fixes -### Documentation +* 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)) -* 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 +## Documentation +* 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)) -## [1.14.0] - 2023-02-17 +## Features -### Added +* 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)) -* Feature: Introduce `MetricsUtils.withMetricsLogger()` utility method (#1000) by @humanzz +## Maintenance -#### Maintenance +* 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)) -* Update logic for recording documentation pages views to use correct runtime name (#1047) by @kozub -* Deps: Bump third party dependencies to the latest versions. +## Pull Requests -### Documentation +* 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 -* Docs: Update Powertools for AWS Lambda (Java) definition by @heitorlessa -* Docs: Add information about other supported langauges to README and docs (#1033) by @kozub -## [1.13.0] - 2022-12-14 +<a name="v1.20.2"></a> +## [v1.20.2] - 2025-05-20 +## Bug Fixes -### Added +* **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)) -* Feature: Idempotency - Handle Lambda timeout scenarios for INPROGRESS records (#933) by @jeromevdl +## Documentation -### Bug Fixes +* Add version policy page and llms.txt, enable privacy plugin, fix formatting ([#1823](https://github.com/aws-powertools/powertools-lambda-java/issues/1823)) -* 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 +## Maintenance -### Documentation +* **automation:** Update automation workflows ([#1779](https://github.com/aws-powertools/powertools-lambda-java/issues/1779)) -* Docs: Add missing grammar article (#976) by @fsmiamoto -## [1.12.3] - 2022-07-12 +<a name="v1.20.1"></a> +## [v1.20.1] - 2025-04-08 +## Bug Fixes -#### Maintenance +* 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)) -* Fixes to resolve vulnerable transitive dependencies ([919](https://github.com/aws-powertools/powertools-lambda-java/issues/919)) +## Documentation +* fix 2 typos +* Correct XML formatting for Maven configuration in Large Messages utility docs -## [1.12.2] - 2022-04-29 +## Maintenance -### Bug Fixes +* Prep release 1.20.1 ([#1817](https://github.com/aws-powertools/powertools-lambda-java/issues/1817)) -* **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.20.0"></a> +## [v1.20.0] - 2025-03-25 +## Features -## [1.12.1] - 2022-04-21 +* **cfn-custom-resource:** Add optional 'reason' field for detailed failure reporting ([#1758](https://github.com/aws-powertools/powertools-lambda-java/issues/1758)) -### Bug Fixes +## Maintenance -* **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)) +* Prep release 1.20.0 ([#1811](https://github.com/aws-powertools/powertools-lambda-java/issues/1811)) -### Maintenance +<a name="v1.19.0"></a> +## [v1.19.0] - 2025-03-07 +## Bug Fixes -* **deps**: Bump third party dependencies to the latest versions. +* 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)) +## Documentation -## [1.12.0] - 2022-03-01 +* 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)) -### 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) +## Features -### 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) +* **build:** remove java 8 support in v2 ([#1606](https://github.com/aws-powertools/powertools-lambda-java/issues/1606)) +* **ci:** Add OSV +## Maintenance -## [1.11.0] - 2022-02-16 +* 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 -### 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) +## Pull Requests +* Merge pull request [#1720](https://github.com/aws-powertools/powertools-lambda-java/issues/1720) from aws-powertools/chore/docs_script_self -## [1.10.3] - 2022-02-01 -### Bug Fixes +<a name="v1.18.0"></a> +## [v1.18.0] - 2023-11-16 +## Bug Fixes -* **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) +* 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)) -### Documentation +## 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)) -* **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. +## Features +* 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)) -## [1.10.2] - 2022-01-07 +## Maintenance -* **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) +* 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)) -## [1.10.1] - 2022-01-06 -* **Logging**: Upgrade Log4j to version 2.17.1 for [CVE-2021-44832](https://nvd.nist.gov/vuln/detail/CVE-2021-44832) +<a name="v1.17.0"></a> +## [v1.17.0] - 2023-08-21 +## Bug Fixes -## [1.10.0] - 2021-12-27 +* 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)) -* **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) +## Documentation -## [1.9.0] - 2021-12-21 +* 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)) -* **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) +## Features -## [1.8.2] - 2021-12-15 +* 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)) -## Security +## Maintenance -* Upgrading Log4j to version 2.16.0 for [CVE-2021-45046](https://nvd.nist.gov/vuln/detail/CVE-2021-45046) +* 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.8.1] - 2021-12-10 -## Security +<a name="v1.16.1"></a> +## [v1.16.1] - 2023-07-19 +## Bug Fixes -* Upgrading Log4j to version 2.15.0 for [CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228) +* 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)) -## [1.8.0] - 2021-11-05 +## Documentation -### Added +* 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)) -* **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) +## Maintenance -### Regression +* **unit-test:** Add missing unit tests in modules with low coverage ([#1264](https://github.com/aws-powertools/powertools-lambda-java/issues/1264)) -* **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 +<a name="v1.16.0"></a> +## [v1.16.0] - 2023-06-29 +## Bug Fixes -* **deps**: Bump third party dependencies to the latest versions. +* 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 -## [1.7.3] - 2021-09-14 +## Features -* **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) +* 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 -## [1.7.2] - 2021-08-03 +## Maintenance -* **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) +* 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)) -## [1.7.1] - 2021-07-06 -* **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)). +<a name="v1.15.0"></a> +## [v1.15.0] - 2023-03-21 +## Bug Fixes -## [1.7.0] - 2021-07-05 +* **cloudformation-module:** Use physicalResourceId when not provided by custom resource ([#1082](https://github.com/aws-powertools/powertools-lambda-java/issues/1082)) -### Added +## Documentation -* **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)). +* **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)) -### Maintenance +## Features -* **deps**: Bump third party dependencies to the latest versions. +* Add DynamoDB provider to parameters module ([#1091](https://github.com/aws-powertools/powertools-lambda-java/issues/1091)) -## [1.6.0] - 2021-06-21 +## 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)) -* **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 +<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.5.0] - 2021-03-30 +## Features -* **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. +* **metrics:** introduce MetricsUtils.withMetricsLogger utility ([#1000](https://github.com/aws-powertools/powertools-lambda-java/issues/1000)) -## [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()`. - +## Maintenance -* **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. +* update the project version to 1.14.0 ([#1052](https://github.com/aws-powertools/powertools-lambda-java/issues/1052)) -## [1.3.0] - 2021-03-05 -* **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 +<a name="v1.13.0"></a> +## [v1.13.0] - 2022-12-14 +## Bug Fixes + +* envelope is not taken into account with built-in types ([#960](https://github.com/aws-powertools/powertools-lambda-java/issues/960)) + +## Documentation + +* add missing grammar article ([#976](https://github.com/aws-powertools/powertools-lambda-java/issues/976)) + +## Features + +* **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 From a43da0a492dcf89bd23cb556f88653c34014b61b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:46:03 +0200 Subject: [PATCH 0905/1008] chore: bump org.codehaus.mojo:exec-maven-plugin from 3.5.1 to 3.6.2 (#2208) Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.5.1 to 3.6.2. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.5.1...3.6.2) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-version: 3.6.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 800d896d0..c96236480 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -25,7 +25,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.5.1</version> + <version>3.6.2</version> <configuration> <mainClass>cdk.CdkApp</mainClass> </configuration> diff --git a/pom.xml b/pom.xml index 8bed88cd0..82b713b10 100644 --- a/pom.xml +++ b/pom.xml @@ -449,7 +449,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> - <version>3.5.1</version> + <version>3.6.2</version> </plugin> </plugins> </pluginManagement> From bcda58fc9eff74ed2b6b86b0c840ec67fc407ce8 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 21 Oct 2025 13:55:26 +0200 Subject: [PATCH 0906/1008] chore(ci): Fix sha256 formatting in git-chglog docker image reference (#2218) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6a3e415b..a2263aa89 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -243,7 +243,7 @@ jobs: 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 + 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 From 326f4b8d39dfab59d24aaea34112535be0f56cc6 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 21 Oct 2025 14:31:24 +0200 Subject: [PATCH 0907/1008] chore(ci): Move tag step before changelog generation. (#2219) --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2263aa89..13c7c34d2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -239,6 +239,11 @@ jobs: 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: | @@ -254,11 +259,6 @@ jobs: --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 }} - - id: tag - name: Create tag - run: | - git tag -a v${{ inputs.version }} -m "Release v${{ inputs.version }}" - git push origin v${{ inputs.version }} docs: runs-on: ubuntu-latest From 07db6313b4f6133546cc449a1fc4c069d0538d76 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:27:07 +0200 Subject: [PATCH 0908/1008] chore(ci): bump version to 2.5.0 (#2220) * chore(ci): bump version to 2.5.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page <pagejep@amazon.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 58 files changed, 65 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 5e0266744..b53d5aeb1 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index b892f43ce..71d924c26 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index c31277d6a..2a6579074 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 262a6b14f..b26007d44 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.4.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.5.0718 ``` ### Deploy with CDK @@ -32,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-2.4.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.5.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index 0f06fe084..b577855ce 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.4.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.5.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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 39f09da04..ad4cd2265 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 677a2b4b6..991c6bc60 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index c96236480..6b6b1b699 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.220.0</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 1325eadaf..9b89d923a 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.4.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.4.0' - aspect 'software.amazon.lambda:powertools-metrics:2.4.0' + aspect 'software.amazon.lambda:powertools-tracing:2.5.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.5.0' + aspect 'software.amazon.lambda:powertools-metrics:2.5.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 98c41093f..85ec540a9 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.4.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.4.0") - aspect("software.amazon.lambda:powertools-metrics:2.4.0") + aspect("software.amazon.lambda:powertools-tracing:2.5.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.5.0") + aspect("software.amazon.lambda:powertools-metrics:2.5.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 64649fe45..6ae05168c 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 3647509c6..bcfcbba6d 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index b5236b11e..a7af92c94 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 95fcc07c1..8bef2d669 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 956b8a9d9..89703a154 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-idempotency-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM</name> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index 8bc2a6311..b4120a082 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index d7778c896..1eb55ae36 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 45d272f70..e2448c879 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 09a67104f..5d7ddf089 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 636435fbb..f496570cc 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 2bdaafbd1..683faf783 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 303ce73cb..b84fac5bb 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.4.0</version> + <version>2.5.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index c75fa90e7..62e211728 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -127,7 +127,7 @@ extra_javascript: extra: powertools: - version: 2.4.0 + version: 2.5.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 82b713b10..6c54d8635 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 850be6922..edb849cea 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index d49fcc2bc..5e40ee503 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index d68bcb63a..d78e84970 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 99ce7d3c4..21161f9e7 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 79fd5b8e0..7c0e957fe 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 3aa6c5bee..469a9a48a 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 4261ff459..1e89edd67 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index 792e19fba..dddc84152 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-logging-log4j</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index ac643b969..4f3a28c72 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-logging-logback</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 54f9015c5..84006df9a 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 957242f0f..e0a58f9bd 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index b67207e75..da5fd7974 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 0a88cc92c..5f6943ca6 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index dcecfc0a8..e9e8f5c47 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index f4e9ff341..21e80c20e 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 20d28895e..9186ff04e 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 096c6ea07..8961701d3 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index b2a6459a5..e982a075e 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 885c9c9cd..a1115c20c 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index a6a090b5d..a8d272b15 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index b78fa225b..a405941d7 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 14cc8898d..39fbc4386 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 2b85a3752..a25c00a1b 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 17e39dda7..e376db085 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 58eab8a45..d4cdb7ed6 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index f7347c03a..7fca07497 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index b2c4f3426..560f4b2d9 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index e03227eb8..bca6bf7d4 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 6a576f529..6e450a93e 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 7758fd906..d7c6e52a8 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 136b2ef43..41ea635de 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.4.0</version> + <version>2.5.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 2cc8fa5d4..5d3ff8874 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 045e76cbf..e53a7c080 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 4194b4240..57f8f25fb 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.4.0</version> + <version>2.5.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From b474aef9dada2c6503263c3a4e326aeb9b770fc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:02:49 +0200 Subject: [PATCH 0909/1008] chore: bump aws.sdk.version from 2.35.8 to 2.35.10 (#2214) Bumps `aws.sdk.version` from 2.35.8 to 2.35.10. Updates `software.amazon.awssdk:bom` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:http-client-spi` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.35.10 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.35.10 Updates `software.amazon.awssdk:dynamodb` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:lambda` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.35.10 Updates `software.amazon.awssdk:cloudwatch` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:xray` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.35.10 Updates `software.amazon.awssdk:cloudformation` from 2.35.8 to 2.35.10 Updates `software.amazon.awssdk:sts` from 2.35.8 to 2.35.10 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.35.10 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.35.10 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.35.10 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.35.10 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index ad4cd2265..043949b08 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.35.8</aws.sdk.version> + <aws.sdk.version>2.35.10</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 6c54d8635..2cada7d19 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.35.8</aws.sdk.version> + <aws.sdk.version>2.35.10</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index da5fd7974..a31458ba9 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> <maven.compiler.version>3.14.0</maven.compiler.version> - <aws.sdk.version>2.35.8</aws.sdk.version> + <aws.sdk.version>2.35.10</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 5e94a4166f44feeede0b6352f4c31376b4248728 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:33:18 +0200 Subject: [PATCH 0910/1008] chore: bump avro.version from 1.12.0 to 1.12.1 (#2215) Bumps `avro.version` from 1.12.0 to 1.12.1. Updates `org.apache.avro:avro` from 1.12.0 to 1.12.1 Updates `org.apache.avro:avro-maven-plugin` from 1.12.0 to 1.12.1 --- updated-dependencies: - dependency-name: org.apache.avro:avro dependency-version: 1.12.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.avro:avro-maven-plugin dependency-version: 1.12.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 1eb55ae36..210371fdf 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -11,7 +11,7 @@ <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.0</avro.version> + <avro.version>1.12.1</avro.version> <protobuf.version>4.33.0</protobuf.version> </properties> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index a8d272b15..fd001f575 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -35,7 +35,7 @@ <properties> <kafka-clients.version>4.1.0</kafka-clients.version> - <avro.version>1.12.0</avro.version> + <avro.version>1.12.1</avro.version> <protobuf.version>4.33.0</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> From 78f158c32315acf20a378de7e92e4b6c9baab1a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:33:37 +0200 Subject: [PATCH 0911/1008] chore: bump com.github.spotbugs:spotbugs-maven-plugin (#2216) Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.7.0 to 4.9.8.1. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.7.0...spotbugs-maven-plugin-4.9.8.1) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.8.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2cada7d19..15bbf8429 100644 --- a/pom.xml +++ b/pom.xml @@ -598,7 +598,7 @@ <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> - <version>4.9.7.0</version> + <version>4.9.8.1</version> <executions> <execution> <id>test</id> From b1b976ee3c9e1f042f6988437b629673dd51952b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:34:04 +0200 Subject: [PATCH 0912/1008] chore: bump org.apache.maven.plugins:maven-compiler-plugin (#2217) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.14.0 to 3.14.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.0...maven-compiler-plugin-3.14.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 6b6b1b699..495e3556a 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -16,7 +16,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.14.0</version> + <version>3.14.1</version> <configuration> <source>11</source> <target>11</target> diff --git a/pom.xml b/pom.xml index 15bbf8429..7163e1da4 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ <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.0</maven-compiler-plugin.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.2.5</maven-surefire-plugin.version> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index a31458ba9..efb6929cc 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -18,7 +18,7 @@ <lambda.java.events>3.16.1</lambda.java.events> <maven.shade.version>3.6.0</maven.shade.version> <aspectj.plugin.version>1.14.1</aspectj.plugin.version> - <maven.compiler.version>3.14.0</maven.compiler.version> + <maven.compiler.version>3.14.1</maven.compiler.version> <aws.sdk.version>2.35.10</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 9186ff04e..0b11ce4e0 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -197,7 +197,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.14.0</version> + <version>3.14.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> From 9316011b3cb27d149cc5d5125712af854090e9b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 17:34:30 +0200 Subject: [PATCH 0913/1008] chore: bump org.graalvm.buildtools:native-maven-plugin (#2225) Bumps [org.graalvm.buildtools:native-maven-plugin](https://github.com/graalvm/native-build-tools) from 0.11.0 to 0.11.2. - [Release notes](https://github.com/graalvm/native-build-tools/releases) - [Commits](https://github.com/graalvm/native-build-tools/compare/0.11.0...0.11.2) --- updated-dependencies: - dependency-name: org.graalvm.buildtools:native-maven-plugin dependency-version: 0.11.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-parameters/powertools-parameters-appconfig/pom.xml | 2 +- powertools-parameters/powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 043949b08..9b8d48912 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -159,7 +159,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 6ae05168c..f35757cce 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -158,7 +158,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 89703a154..a86387b94 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -139,7 +139,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index e2448c879..49a6352c0 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -176,7 +176,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index f496570cc..cdf131482 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -61,7 +61,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 5e40ee503..f6d8ad33f 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -159,7 +159,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index d78e84970..9b1639b31 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -133,7 +133,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index efb6929cc..4d9d49d0b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -199,7 +199,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index e982a075e..64907072f 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -114,7 +114,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index a1115c20c..a55947d4b 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -143,7 +143,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 39fbc4386..8c26d5be9 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -143,7 +143,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index a25c00a1b..94952eba5 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -132,7 +132,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index e376db085..f106970b9 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -130,7 +130,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index d4cdb7ed6..74b4ca7c4 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -188,7 +188,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 7fca07497..95acb65b2 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -118,7 +118,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 560f4b2d9..e8757f28d 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -142,7 +142,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index bca6bf7d4..099627265 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -143,7 +143,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 6e450a93e..cbf07e345 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -143,7 +143,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index d7c6e52a8..64d6cefcd 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -157,7 +157,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 41ea635de..40a942515 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -144,7 +144,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 5d3ff8874..457feec23 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -120,7 +120,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index e53a7c080..2b2f9b5be 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -157,7 +157,7 @@ <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> - <version>0.11.0</version> + <version>0.11.2</version> <extensions>true</extensions> <executions> <execution> From fea5d4a26d3a044d1bcb0d726769cac04ad21e53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 17:35:22 +0200 Subject: [PATCH 0914/1008] chore: bump sam/build-java21 (#2226) Bumps sam/build-java21 from `e12c501` to `068d2ab`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: '068d2ab1eee017c2557b7a3879a9f9cc79ba0d5d8b177af5949437eee96458cd' dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 7dc04ec90..c537351c8 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:e12c501aa7d3d3dc04be0d900115a0339c73f523ad73a9aecd9e99499413c465 +FROM public.ecr.aws/sam/build-java21@sha256:068d2ab1eee017c2557b7a3879a9f9cc79ba0d5d8b177af5949437eee96458cd # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From eace667e609f232f26121ecb8dec254164c9172e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 17:35:51 +0200 Subject: [PATCH 0915/1008] chore: bump aws.sdk.version from 2.35.10 to 2.35.11 (#2222) Bumps `aws.sdk.version` from 2.35.10 to 2.35.11. Updates `software.amazon.awssdk:bom` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:http-client-spi` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.35.11 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.35.11 Updates `software.amazon.awssdk:dynamodb` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:lambda` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.35.11 Updates `software.amazon.awssdk:cloudwatch` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:xray` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.35.11 Updates `software.amazon.awssdk:cloudformation` from 2.35.10 to 2.35.11 Updates `software.amazon.awssdk:sts` from 2.35.10 to 2.35.11 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.35.11 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.35.11 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.35.11 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.35.11 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 9b8d48912..21c16891d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.35.10</aws.sdk.version> + <aws.sdk.version>2.35.11</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 7163e1da4..2d9b7db00 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.35.10</aws.sdk.version> + <aws.sdk.version>2.35.11</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 4d9d49d0b..8ba1d76cd 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <maven.shade.version>3.6.0</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.35.10</aws.sdk.version> + <aws.sdk.version>2.35.11</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 16924198fb70dd882bb662dd30b89bd2d2a06058 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 17:38:31 +0200 Subject: [PATCH 0916/1008] chore: bump org.jacoco:jacoco-maven-plugin from 0.8.13 to 0.8.14 (#2224) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.13 to 0.8.14. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.13...v0.8.14) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2d9b7db00..1493020d4 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ <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.3</maven-surefire-plugin.version> - <jacoco-maven-plugin.version>0.8.13</jacoco-maven-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.11.3</maven-javadoc-plugin.version> <maven-source-plugin.version>3.3.1</maven-source-plugin.version> From e249cef0dd4f806c3203065f453762aceff5d56f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:07:15 +0200 Subject: [PATCH 0917/1008] chore: bump org.junit:junit-bom from 5.13.4 to 5.14.0 (#2199) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1493020d4..b9c3ec419 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ <maven-javadoc-plugin.version>3.11.3</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.13.4</junit.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> From aae50c9d1952c22efda4e8ab9196c3856d11bde2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:42:21 +0100 Subject: [PATCH 0918/1008] chore: bump org.apache.maven.plugins:maven-shade-plugin (#2230) Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.6.0...maven-shade-plugin-3.6.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- examples/powertools-examples-core-utilities/serverless/pom.xml | 2 +- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 2a6579074..eac85eafe 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -120,7 +120,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 21c16891d..bc7bc5ece 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -116,7 +116,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 991c6bc60..8a5ac6601 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -104,7 +104,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index f35757cce..71b93be66 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -116,7 +116,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index bcfcbba6d..60d6b796f 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -90,7 +90,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index a7af92c94..01e734f6f 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -91,7 +91,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 8bef2d669..d4aa3e257 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -91,7 +91,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index a86387b94..6ee70def3 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -96,7 +96,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index b4120a082..d84651e15 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -107,7 +107,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 210371fdf..fe56c6b1e 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -64,7 +64,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 49a6352c0..caf75943f 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -134,7 +134,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.6.0</version> + <version>3.6.1</version> <executions> <execution> <phase>package</phase> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 8ba1d76cd..90740d364 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -16,7 +16,7 @@ <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.0</maven.shade.version> + <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.35.11</aws.sdk.version> From cc21a1421674f95d46824b043177f46095a88101 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:43:23 +0100 Subject: [PATCH 0919/1008] chore: bump aws.sdk.version from 2.35.11 to 2.36.0 (#2227) Bumps `aws.sdk.version` from 2.35.11 to 2.36.0. Updates `software.amazon.awssdk:bom` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:http-client-spi` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.36.0 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.36.0 Updates `software.amazon.awssdk:dynamodb` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:lambda` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.36.0 Updates `software.amazon.awssdk:cloudwatch` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:xray` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.36.0 Updates `software.amazon.awssdk:cloudformation` from 2.35.11 to 2.36.0 Updates `software.amazon.awssdk:sts` from 2.35.11 to 2.36.0 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.36.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.36.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.36.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.36.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index bc7bc5ece..b9c7e9fd8 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.35.11</aws.sdk.version> + <aws.sdk.version>2.36.0</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index b9c3ec419..7fc665854 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.35.11</aws.sdk.version> + <aws.sdk.version>2.36.0</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 90740d364..337316d58 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.35.11</aws.sdk.version> + <aws.sdk.version>2.36.0</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From fdbf448bc390188616ae2690ec105c20d2eb1be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:46:19 +0100 Subject: [PATCH 0920/1008] chore: bump org.apache.maven.plugins:maven-surefire-plugin (#2228) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.3 to 3.5.4. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.3...surefire-3.5.4) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/terraform/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- pom.xml | 4 ++-- powertools-common/pom.xml | 2 +- powertools-logging/pom.xml | 4 ++-- powertools-logging/powertools-logging-log4j/pom.xml | 4 ++-- powertools-logging/powertools-logging-logback/pom.xml | 4 ++-- powertools-parameters/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-appconfig/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-dynamodb/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-secrets/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-ssm/pom.xml | 4 ++-- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- 16 files changed, 25 insertions(+), 25 deletions(-) diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index d4aa3e257..4b7d19fe4 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -126,7 +126,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index caf75943f..e72df3086 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -99,7 +99,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.5.3</version> + <version>3.5.4</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 5d7ddf089..e2ee15f26 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -73,7 +73,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.5.3</version> + <version>3.5.4</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/pom.xml b/pom.xml index 7fc665854..7a884ce12 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ <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.3</maven-surefire-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.11.3</maven-javadoc-plugin.version> @@ -625,7 +625,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> @{argLine} diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 9b1639b31..3583a4dc0 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -106,7 +106,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 8c26d5be9..b08a4b695 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -123,7 +123,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -182,7 +182,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <AWS_LAMBDA_LOG_FORMAT>JSON</AWS_LAMBDA_LOG_FORMAT> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 94952eba5..b4800ab54 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -112,7 +112,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -204,7 +204,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <POWERTOOLS_SERVICE_NAME>testLog4j</POWERTOOLS_SERVICE_NAME> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index f106970b9..0cfbb6104 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -110,7 +110,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -203,7 +203,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <POWERTOOLS_SERVICE_NAME>testLogback</POWERTOOLS_SERVICE_NAME> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 95acb65b2..f1e4eae57 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -80,7 +80,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> @@ -98,7 +98,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index e8757f28d..eefc46ac5 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -108,7 +108,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -175,7 +175,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 099627265..cfc8f64d3 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -109,7 +109,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -176,7 +176,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index cbf07e345..9d3ca0500 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -109,7 +109,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -176,7 +176,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 64d6cefcd..a277337a7 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -98,7 +98,7 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> @@ -123,7 +123,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 40a942515..61fb4c3b9 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -110,7 +110,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 457feec23..89fb2056a 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -103,7 +103,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 2b2f9b5be..c1f1433a1 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -137,7 +137,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.3</version> + <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent From d262d3a3ea30bd3c611ff55253086dbc3a8c612f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:54:22 +0100 Subject: [PATCH 0921/1008] chore: bump aws.sdk.version from 2.36.0 to 2.36.1 (#2231) Bumps `aws.sdk.version` from 2.36.0 to 2.36.1. Updates `software.amazon.awssdk:bom` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:http-client-spi` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.36.1 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.36.1 Updates `software.amazon.awssdk:dynamodb` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:lambda` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.36.1 Updates `software.amazon.awssdk:cloudwatch` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:xray` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.36.1 Updates `software.amazon.awssdk:cloudformation` from 2.36.0 to 2.36.1 Updates `software.amazon.awssdk:sts` from 2.36.0 to 2.36.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.36.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.36.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.36.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.36.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index b9c7e9fd8..b37c468c0 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.36.0</aws.sdk.version> + <aws.sdk.version>2.36.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 7a884ce12..a7eac97fd 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.36.0</aws.sdk.version> + <aws.sdk.version>2.36.1</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 337316d58..fd2a3bae1 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.36.0</aws.sdk.version> + <aws.sdk.version>2.36.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 7a5d0c95c4d4dd0381323d80a2bff61fde423a4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:55:00 +0100 Subject: [PATCH 0922/1008] chore: bump org.junit.jupiter:junit-jupiter from 5.13.4 to 5.14.0 (#2232) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework) from 5.13.4 to 5.14.0. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.4...r5.14.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-version: 5.14.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 495e3556a..31d5f7e99 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -9,7 +9,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.220.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.13.4</junit.version> + <junit.version>5.14.0</junit.version> </properties> <build> <plugins> From 5fcb05f16e21feff635fe034abbffebe126b847e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:55:23 +0100 Subject: [PATCH 0923/1008] chore: bump sam/build-java21 (#2233) Bumps sam/build-java21 from `068d2ab` to `72709a0`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 72709a010ebfc993fb402c1bce599a0b754110b873d1a58e60c52136e8c8f3f1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index c537351c8..8d26b4770 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:068d2ab1eee017c2557b7a3879a9f9cc79ba0d5d8b177af5949437eee96458cd +FROM public.ecr.aws/sam/build-java21@sha256:72709a010ebfc993fb402c1bce599a0b754110b873d1a58e60c52136e8c8f3f1 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 1e5166092bb5ec99e6dd60906bf11cd10bcf591a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 09:53:57 +0100 Subject: [PATCH 0924/1008] chore: bump org.apache.maven.plugins:maven-javadoc-plugin (#2223) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.3 to 3.12.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.3...maven-javadoc-plugin-3.12.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7eac97fd..8a8474f0a 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ <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.11.3</maven-javadoc-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> From 1296a9d93da866def5789fb54f0492f15fbbf4db Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 27 Oct 2025 12:13:15 +0100 Subject: [PATCH 0925/1008] feat(logging): Support functional interface in addition to AspectJ `@Logging` annotation (#2205) * Initial version of PowertoolsLogging.initializeLogging API. * Add logging-function E2E tests for new PowertoolsLogging.initializeLogging functional interface. * Add service name env var for powertools-logging unit tests. * Catch NoSuchFileException instead of IOException base class in KeyBufferTest.java. * Catch NoSuchFileException also on cleanup since order of test execution is not guaranteed. * Fix spotbugs issue. This is intended design to support unit tests. The class is in internal package. * Reduce cognitive complexity of LambdaLoggingAspect. * Suppress sonar findings needed for internal unit tests. * Add naming convention findings to sonar ignore list. * Make PowertoolsLogging thread-safe. * Make cold start tracking thread-safe. * Update protobuf generated classes after version bump. * Add merged version bump to non-release e2e logging function handler. * Add withLogging callback based interface to PowertoolsLogging. --- .../demo/kafka/protobuf/ProtobufProduct.java | 8 +- .../protobuf/ProtobufProductOrBuilder.java | 2 +- .../protobuf/ProtobufProductOuterClass.java | 10 +- .../handlers/logging-functional/pom.xml | 60 +++ .../lambda/powertools/e2e/Function.java | 40 ++ .../amazon/lambda/powertools/e2e/Input.java | 38 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 +++ .../resource-config.json | 19 + .../reflect-config.json | 25 + .../reflect-config.json | 20 + .../resource-config.json | 7 + .../src/main/resources/log4j2.xml | 17 + powertools-e2e-tests/handlers/pom.xml | 6 + .../amazon/lambda/powertools/LoggingE2ET.java | 2 +- powertools-logging/pom.xml | 1 + .../PowerToolsResolverFactoryTest.java | 5 + .../internal/LambdaEcsEncoderTest.java | 22 +- .../internal/LambdaJsonEncoderTest.java | 6 + .../powertools/logging/PowertoolsLogging.java | 320 +++++++++++- .../logging/internal/LambdaLoggingAspect.java | 214 ++------ .../logging/internal/LoggingConstants.java | 11 +- .../internal/PowertoolsLoggedFields.java | 2 +- .../logging/PowertoolsLoggingTest.java | 466 +++++++++++++++++- .../internal/LambdaLoggingAspectTest.java | 156 +----- spotbugs-exclude.xml | 3 +- 29 files changed, 1232 insertions(+), 349 deletions(-) create mode 100644 powertools-e2e-tests/handlers/logging-functional/pom.xml create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/logging-functional/src/main/resources/log4j2.xml 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 index e36810e08..13d8905f2 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.32.1 +// Protobuf Java Version: 4.33.0 package org.demo.kafka.protobuf; @@ -18,10 +18,10 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 32, - /* patch= */ 1, + /* minor= */ 33, + /* patch= */ 0, /* suffix= */ "", - ProtobufProduct.class.getName()); + "ProtobufProduct"); } // Use ProtobufProduct.newBuilder() to construct. private ProtobufProduct(com.google.protobuf.GeneratedMessage.Builder<?> builder) { 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 index 951644b2f..ae7cb2182 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.32.1 +// Protobuf Java Version: 4.33.0 package org.demo.kafka.protobuf; 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 index 0da2eec57..4fcac7bd7 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.32.1 +// Protobuf Java Version: 4.33.0 package org.demo.kafka.protobuf; @@ -12,10 +12,10 @@ private ProtobufProductOuterClass() {} com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 32, - /* patch= */ 1, + /* minor= */ 33, + /* patch= */ 0, /* suffix= */ "", - ProtobufProductOuterClass.class.getName()); + "ProtobufProductOuterClass"); } public static void registerAllExtensions( com.google.protobuf.ExtensionRegistryLite registry) { @@ -51,7 +51,7 @@ public static void registerAllExtensions( new com.google.protobuf.Descriptors.FileDescriptor[] { }); internal_static_org_demo_kafka_protobuf_ProtobufProduct_descriptor = - getDescriptor().getMessageTypes().get(0); + 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, 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..5d774fe21 --- /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.5.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-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..78ab9ba4b --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/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.PowertoolsLogging; + +public class Function implements RequestHandler<Input, String> { + private static final Logger LOG = LoggerFactory.getLogger(Function.class); + + 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-functional/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 new file mode 100644 index 000000000..66fd49ddc --- /dev/null +++ b/powertools-e2e-tests/handlers/logging-functional/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-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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index fd2a3bae1..752492d6d 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -30,6 +30,7 @@ <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> @@ -62,6 +63,11 @@ <artifactId>powertools-logging-logback</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-tracing</artifactId> 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 f5d2cea84..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 @@ -70,7 +70,7 @@ void tearDown() { } @ParameterizedTest - @ValueSource(strings = { "logging-log4j", "logging-logback" }) + @ValueSource(strings = { "logging-log4j", "logging-logback", "logging-functional" }) @Timeout(value = 15, unit = TimeUnit.MINUTES) void test_logInfoWithAdditionalKeys(String pathToFunction) throws JsonProcessingException { setupInfrastructure(pathToFunction); diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index b08a4b695..2fa4680fb 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -186,6 +186,7 @@ <configuration> <environmentVariables> <AWS_LAMBDA_LOG_FORMAT>JSON</AWS_LAMBDA_LOG_FORMAT> + <POWERTOOLS_SERVICE_NAME>testService</POWERTOOLS_SERVICE_NAME> </environmentVariables> </configuration> </plugin> 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 index 7eeffc78f..46b5b65d4 100644 --- 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 @@ -24,6 +24,7 @@ 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; @@ -35,6 +36,7 @@ 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) @@ -45,7 +47,10 @@ class PowerToolsResolverFactoryTest { @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 { 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 index 898abec2e..74a11813b 100644 --- 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 @@ -18,11 +18,6 @@ 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 java.io.File; import java.io.IOException; import java.nio.channels.FileChannel; @@ -30,16 +25,26 @@ 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 software.amazon.lambda.powertools.common.stubs.TestLambdaContext; 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.logging.logback.LambdaEcsEncoder; +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 { @@ -51,7 +56,10 @@ class LambdaEcsEncoderTest { @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 { 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 index 326fa4b19..97770d3d0 100644 --- 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 @@ -43,6 +43,8 @@ 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; @@ -52,6 +54,7 @@ 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; @@ -73,7 +76,10 @@ class LambdaJsonEncoderTest { @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 { 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 index 1276c2a87..f4c18af64 100644 --- 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 @@ -14,21 +14,104 @@ 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 backend-independent API for log buffering operations. - * This class abstracts away the underlying logging framework (Log4j2, Logback) and - * provides a unified interface for buffer management. + * 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. @@ -52,4 +135,235 @@ public static void clearBuffer() { ((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/internal/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java index 591283996..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,25 +15,14 @@ package software.amazon.lambda.powertools.logging.internal; import static java.nio.charset.StandardCharsets.UTF_8; -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.getXrayTraceId; -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.placedOnRequestHandler; import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnStreamHandler; -import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.serviceName; import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; -import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_LEVEL; 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_LEVEL; import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_LOG_RESPONSE; -import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.POWERTOOLS_SAMPLING_RATE; -import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; -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.SERVICE; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -42,9 +31,6 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.util.Arrays; -import java.util.Locale; -import java.util.Random; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -53,66 +39,22 @@ import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; import org.slf4j.MarkerFactory; -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.Logging; +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 = LoggerFactory.getLogger(LambdaLoggingAspect.class); - private static final Random SAMPLER = new Random(); - private static Level LEVEL_AT_INITIALISATION; /* not final for test purpose */ - private static final LoggingManager LOGGING_MANAGER; static { LOGGING_MANAGER = LoggingManagerRegistry.getLoggingManager(); - - setLogLevel(); - - LEVEL_AT_INITIALISATION = LOGGING_MANAGER.getLogLevel(LOG); - } - - static void setLogLevel() { - 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); - } - } - setLogLevels(powertoolsLevel); - } else if (LAMBDA_LOG_LEVEL != null) { - setLogLevels(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 setLogLevels(Level logLevel) { - LOGGING_MANAGER.setLogLevel(logLevel); } @SuppressWarnings({ "EmptyMethod" }) @@ -131,17 +73,19 @@ public Object around(ProceedingJoinPoint pjp, boolean isOnRequestHandler = placedOnRequestHandler(pjp); boolean isOnRequestStreamHandler = placedOnStreamHandler(pjp); - setLogLevelBasedOnSamplingRate(pjp, logging); - addLambdaContextToLoggingContext(pjp); - getXrayTraceId().ifPresent(xRayTraceId -> MDC.put(FUNCTION_TRACE_ID.getName(), xRayTraceId)); - - Object[] proceedArgs = logEvent(pjp, logging, isOnRequestHandler, isOnRequestStreamHandler); + // Initialize logging using PowertoolsLogging + Context context = extractContext(pjp); + Object[] proceedArgs = pjp.getArgs(); - if (!logging.correlationIdPath().isEmpty()) { - captureCorrelationId(logging.correlationIdPath(), proceedArgs, isOnRequestHandler, - isOnRequestStreamHandler); + if (isHandlerMethod(pjp) && context != null) { + Object event = extractEventForCorrelationId(logging, isOnRequestHandler, isOnRequestStreamHandler, + proceedArgs); + PowertoolsLogging.initializeLogging(context, logging.samplingRate(), + logging.correlationIdPath().isEmpty() ? null : logging.correlationIdPath(), event); } + logEvent(pjp, logging, isOnRequestHandler, isOnRequestStreamHandler, proceedArgs); + @SuppressWarnings("PMD.CloseResource") // Lambda-owned stream, not ours to close OutputStream backupOutputStream = null; if (isOnRequestStreamHandler) { @@ -160,7 +104,7 @@ public Object around(ProceedingJoinPoint pjp, handleException(pjp, logging, t); throw t; } finally { - performCleanup(logging); + PowertoolsLogging.clearState(logging.clearState()); } logResponse(pjp, logging, lambdaFunctionResponse, isOnRequestHandler, isOnRequestStreamHandler, @@ -169,74 +113,18 @@ public Object around(ProceedingJoinPoint pjp, return lambdaFunctionResponse; } - private Object[] logEvent(ProceedingJoinPoint pjp, Logging logging, - boolean isOnRequestHandler, boolean isOnRequestStreamHandler) { - Object[] proceedArgs = pjp.getArgs(); + private void logEvent(ProceedingJoinPoint pjp, Logging logging, + boolean isOnRequestHandler, boolean isOnRequestStreamHandler, Object[] proceedArgs) { if (logging.logEvent() || POWERTOOLS_LOG_EVENT) { if (isOnRequestHandler) { - logRequestHandlerEvent(pjp, pjp.getArgs()[0]); + logRequestHandlerEvent(pjp, proceedArgs[0]); } else if (isOnRequestStreamHandler) { - proceedArgs = logRequestStreamHandlerEvent(pjp); - } - } - return proceedArgs; - } - - private void addLambdaContextToLoggingContext(ProceedingJoinPoint pjp) { - Context extractedContext = extractContext(pjp); - - if (extractedContext != null) { - PowertoolsLoggedFields.setValuesFromLambdaContext(extractedContext).forEach(MDC::put); - MDC.put(FUNCTION_COLD_START.getName(), isColdStart() ? "true" : "false"); - MDC.put(SERVICE.getName(), serviceName()); - } - } - - private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, - final Logging logging) { - double samplingRate = samplingRate(logging); - - if (isHandlerMethod(pjp)) { - - if (samplingRate < 0 || samplingRate > 1) { - LOG.warn("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", - samplingRate); - return; - } - - MDC.put(PowertoolsLoggedFields.SAMPLING_RATE.getName(), String.valueOf(samplingRate)); - - if (samplingRate == 0) { - return; - } - - float sample = SAMPLER.nextFloat(); - - if (samplingRate > sample) { - setLogLevels(Level.DEBUG); - - LOG.debug("Changed log level to DEBUG based on Sampling configuration. " - + "Sampling Rate: {}, Sampler Value: {}.", samplingRate, sample); - } else if (LEVEL_AT_INITIALISATION != LOGGING_MANAGER.getLogLevel(LOG)) { - setLogLevels(LEVEL_AT_INITIALISATION); + logRequestStreamHandlerEvent(pjp, proceedArgs); } } } - private double samplingRate(final Logging logging) { - String sampleRate = POWERTOOLS_SAMPLING_RATE; - if (null != sampleRate) { - try { - return Double.parseDouble(sampleRate); - } catch (NumberFormatException e) { - LOG.warn("Skipping sampling rate on environment variable configuration because of invalid " - + "value. Sampling rate: {}", sampleRate); - } - } - return logging.samplingRate(); - } - @SuppressWarnings("java:S3457") private void logRequestHandlerEvent(final ProceedingJoinPoint pjp, final Object event) { Logger log = logger(pjp); @@ -246,12 +134,11 @@ private void logRequestHandlerEvent(final ProceedingJoinPoint pjp, final Object } @SuppressWarnings("java:S3457") - private Object[] logRequestStreamHandlerEvent(final ProceedingJoinPoint pjp) { - Object[] args = pjp.getArgs(); + private void logRequestStreamHandlerEvent(final ProceedingJoinPoint pjp, Object[] args) { Logger log = logger(pjp); if (log.isInfoEnabled()) { try { - byte[] bytes = bytesFromInputStreamSafely((InputStream) pjp.getArgs()[0]); + 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))); @@ -259,7 +146,6 @@ private Object[] logRequestStreamHandlerEvent(final ProceedingJoinPoint pjp) { LOG.warn("Failed to log event from supplied input stream.", e); } } - return args; } @SuppressWarnings("java:S3457") @@ -279,38 +165,6 @@ private void logRequestStreamHandlerResponse(final ProceedingJoinPoint pjp, fina } } - private void captureCorrelationId(final String correlationIdPath, - Object[] proceedArgs, - final boolean isOnRequestHandler, - final boolean isOnRequestStreamHandler) { - if (isOnRequestHandler) { - JsonNode jsonNode = JsonConfig.get().getObjectMapper().valueToTree(proceedArgs[0]); - setCorrelationIdFromNode(correlationIdPath, jsonNode); - } else if (isOnRequestStreamHandler) { - try { - byte[] bytes = bytesFromInputStreamSafely((InputStream) proceedArgs[0]); - JsonNode jsonNode = JsonConfig.get().getObjectMapper().readTree(bytes); - proceedArgs[0] = new ByteArrayInputStream(bytes); - - setCorrelationIdFromNode(correlationIdPath, jsonNode); - } catch (IOException e) { - LOG.warn("Failed to capture correlation id on event from supplied input stream.", e); - } - } - } - - private void setCorrelationIdFromNode(String correlationIdPath, JsonNode jsonNode) { - Expression<JsonNode> jmesExpression = JsonConfig.get().getJmesPath().compile(correlationIdPath); - JsonNode node = jmesExpression.search(jsonNode); - - String asText = node.asText(); - if (null != asText && !asText.isEmpty()) { - MDC.put(CORRELATION_ID.getName(), asText); - } else { - LOG.warn("Unable to extract any correlation id. Is your function expecting supported event type?"); - } - } - private byte[] bytesFromInputStreamSafely(final InputStream inputStream) throws IOException { try (ByteArrayOutputStream out = new ByteArrayOutputStream(); InputStreamReader reader = new InputStreamReader(inputStream, UTF_8); @@ -348,16 +202,6 @@ private void handleException(ProceedingJoinPoint pjp, Logging logging, Throwable } } - private void performCleanup(Logging logging) { - if (logging.clearState()) { - MDC.clear(); - } - if (LOGGING_MANAGER instanceof BufferManager) { - ((BufferManager) LOGGING_MANAGER).clearBuffer(); - } - coldStartDone(); - } - private void logResponse(ProceedingJoinPoint pjp, Logging logging, Object lambdaFunctionResponse, boolean isOnRequestHandler, boolean isOnRequestStreamHandler, OutputStream backupOutputStream, Object[] proceedArgs) throws IOException { @@ -372,6 +216,28 @@ private void logResponse(ProceedingJoinPoint pjp, Logging logging, Object lambda } } + 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 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 index 989608a77..511b12ace 100644 --- 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 @@ -14,12 +14,15 @@ package software.amazon.lambda.powertools.logging.internal; -class LoggingConstants { - static String LAMBDA_LOG_LEVEL = System.getenv("AWS_LAMBDA_LOG_LEVEL"); /* not final for test purpose */ +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 */ - 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_LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); /* not final for test purpose */ - static String POWERTOOLS_SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE"); /* 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 */ diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java index 6e0047f4f..2545396d2 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java @@ -47,7 +47,7 @@ public static List<String> stringValues() { return Stream.of(values()).map(PowertoolsLoggedFields::getName).collect(Collectors.toList()); } - static Map<String, String> setValuesFromLambdaContext(Context context) { + 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/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggingTest.java index ea3a2f3f6..9c68f687b 100644 --- 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 @@ -14,23 +14,78 @@ 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() { + 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 @@ -50,4 +105,413 @@ void testClearBuffer_shouldCallBufferManager() { // 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/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index f77997a77..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 @@ -31,8 +31,6 @@ import java.io.ByteArrayOutputStream; import java.io.File; 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.NoSuchFileException; @@ -41,6 +39,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -49,8 +48,6 @@ import org.junitpioneer.jupiter.ClearEnvironmentVariable; import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.junitpioneer.jupiter.SetSystemProperty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.slf4j.event.Level; import org.slf4j.test.TestLogger; @@ -67,6 +64,7 @@ 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.PowertoolsLogApiGatewayHttpApiCorrelationId; @@ -90,7 +88,6 @@ class LambdaLoggingAspectTest { - private static final Logger LOG = LoggerFactory.getLogger(LambdaLoggingAspectTest.class); private static final int EXPECTED_CONTEXT_SIZE = 8; private RequestStreamHandler requestStreamHandler; private RequestHandler<Object, Object> requestHandler; @@ -98,13 +95,16 @@ class LambdaLoggingAspectTest { private Context context; @BeforeEach - void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException { + 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(); - resetLogLevel(Level.INFO); writeStaticField(LoggingConstants.class, "LAMBDA_LOG_LEVEL", null, true); writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", null, true); writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_EVENT", false, true); @@ -131,139 +131,10 @@ void cleanUp() throws IOException { } catch (NoSuchFileException e) { // may not be there in the first run } - } - - @Test - void shouldLogDebugWhenPowertoolsLevelEnvVarIsDebug() throws IllegalAccessException { - // GIVEN - writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "DEBUG", true); - - // WHEN - LambdaLoggingAspect.setLogLevel(); - - // THEN - assertThat(LOG.isDebugEnabled()).isTrue(); - } - - @Test - void shouldLogInfoWhenPowertoolsLevelEnvVarIsInfo() throws IllegalAccessException { - // GIVEN - writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INFO", true); - - // WHEN - LambdaLoggingAspect.setLogLevel(); - - // THEN - assertThat(LOG.isDebugEnabled()).isFalse(); - assertThat(LOG.isInfoEnabled()).isTrue(); - } - - @Test - void shouldLogInfoWhenPowertoolsLevelEnvVarIsInvalid() throws IllegalAccessException { - // GIVEN - writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "INVALID", true); - - // WHEN - LambdaLoggingAspect.setLogLevel(); - - // THEN - assertThat(LOG.isDebugEnabled()).isFalse(); - assertThat(LOG.isInfoEnabled()).isTrue(); - } - - @Test - void shouldLogWarnWhenPowertoolsLevelEnvVarIsWarn() throws IllegalAccessException { - // GIVEN - writeStaticField(LoggingConstants.class, "POWERTOOLS_LOG_LEVEL", "WARN", true); - - // WHEN - LambdaLoggingAspect.setLogLevel(); - - // 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 - LambdaLoggingAspect.setLogLevel(); - // 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 - LambdaLoggingAspect.setLogLevel(); - - // 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 - LambdaLoggingAspect.setLogLevel(); - - // 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 - LambdaLoggingAspect.setLogLevel(); - - // 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 - LambdaLoggingAspect.setLogLevel(); - - // THEN - assertThat(LOG.isDebugEnabled()).isFalse(); - assertThat(LOG.isInfoEnabled()).isFalse(); - assertThat(LOG.isWarnEnabled()).isTrue(); + // Reset log level to INFO to ensure test isolation + LoggingManager loggingManager = LoggingManagerRegistry.getLoggingManager(); + loggingManager.setLogLevel(Level.INFO); } @Test @@ -855,11 +726,4 @@ void shouldClearBufferBeforeErrorLoggingWhenFlushBufferOnUncaughtErrorDisabled() } } - private void resetLogLevel(Level level) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - Method setLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("setLogLevels", Level.class); - setLogLevels.setAccessible(true); - setLogLevels.invoke(null, level); - writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); - } } diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 35aed5e26..53226e3c2 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -201,8 +201,7 @@ <Bug pattern="MS_SHOULD_BE_FINAL"/> <Or> <And> - <Class name="software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect"/> - <Field name="SAMPLING_RATE"/> + <Class name="software.amazon.lambda.powertools.logging.internal.LoggingConstants"/> </And> </Or> </Match> From 1a0e7cbce5461abc85e98ae2a938477f203d6203 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:05:05 +0100 Subject: [PATCH 0926/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.220.0 to 2.221.0 (#2235) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.220.0 to 2.221.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.220.0...v2.221.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.221.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 31d5f7e99..42777cc96 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.5.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.220.0</cdk.version> + <cdk.version>2.221.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.14.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0b11ce4e0..046592e1c 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.220.0</cdk.version> + <cdk.version>2.221.0</cdk.version> </properties> <dependencies> From 2240e5511c619a4809b0146feafba4c3ac926e02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:06:15 +0100 Subject: [PATCH 0927/1008] chore: bump aws.sdk.version from 2.32.31 to 2.36.2 (#2237) Bumps `aws.sdk.version` from 2.32.31 to 2.36.2. Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:sdk-core` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.32.31 to 2.36.2 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.36.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.36.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.36.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index eac85eafe..a5d14f0c1 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.32.31</sdk.version> + <sdk.version>2.36.2</sdk.version> </properties> <dependencies> From ec7f84e4da0b31f306f8784717ceccfa3125f3fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:06:36 +0100 Subject: [PATCH 0928/1008] chore: bump actions/upload-artifact from 4.6.2 to 5.0.0 (#2238) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 5.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/ea165f8d65b6e75b540449e92b4886f43607fa02...330a01c490aca151604b8cf639adc76d48f6c5d4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13c7c34d2..2c7f65324 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,7 +112,7 @@ jobs: snapshot: ${{ inputs.snapshot}} - id: upload_source name: Upload artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: if-no-files-found: error name: source diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index d4df1e675..3b17b3700 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -46,7 +46,7 @@ jobs: publish_results: true repo_token: ${{ secrets.SCORECARD_TOKEN }} - name: Upload Results - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: SARIF file path: results.sarif From 7e4fa88a7efacdd5c505ac68533febb9ed04bb8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:06:49 +0100 Subject: [PATCH 0929/1008] chore: bump github/codeql-action from 4.30.9 to 4.31.0 (#2239) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.9 to 4.31.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/16140ae1a102900babc80a33c44059580f687047...4e94bd11f71e507f7f87df81788dff88d1dacbfb) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 3b17b3700..4ca976695 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v3.29.5 + uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v3.29.5 with: sarif_file: results.sarif From b98910fa1fd1d5e54c1a82fbf1ab1d421228de29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:07:12 +0100 Subject: [PATCH 0930/1008] chore: bump actions/download-artifact from 5.0.0 to 6.0.0 (#2234) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/634f93cb2916e3fdff6788551b99b062d0335ce0...018cc2cf5baa6db3ef3c5f8a56943fffe632ef53) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c7f65324..79cb5b04f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 with: name: source - name: Setup Java @@ -168,7 +168,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 with: name: source - name: Setup Java @@ -191,7 +191,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 with: name: source - name: Setup Java @@ -229,7 +229,7 @@ jobs: ref: ${{ env.RELEASE_COMMIT }} - id: download_source name: Download artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4.6.1 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 with: name: source - id: setup-git From 74dc8a0d287382d5647588d9f11844465b8476a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:07:33 +0100 Subject: [PATCH 0931/1008] chore: bump graalvm/setup-graalvm from 1.4.1 to 1.4.2 (#2236) Bumps [graalvm/setup-graalvm](https://github.com/graalvm/setup-graalvm) from 1.4.1 to 1.4.2. - [Release notes](https://github.com/graalvm/setup-graalvm/releases) - [Commits](https://github.com/graalvm/setup-graalvm/compare/2a2412009026a83f51d179f92dc2b3fd4c8142df...eec48106e0bf45f2976c2ff0c3e22395cced8243) --- updated-dependencies: - dependency-name: graalvm/setup-graalvm dependency-version: 1.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 8bc474c20..e1d949aad 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -103,7 +103,7 @@ jobs: powertools-*/** pom.xml - name: Setup GraalVM - uses: graalvm/setup-graalvm@2a2412009026a83f51d179f92dc2b3fd4c8142df # v1.4.1 + uses: graalvm/setup-graalvm@eec48106e0bf45f2976c2ff0c3e22395cced8243 # v1.4.2 with: java-version: "21" distribution: "graalvm" From 1abc57b70da838ee97ccf6f17096d37c2993b6e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:22:36 +0100 Subject: [PATCH 0932/1008] chore: bump aws.sdk.version from 2.36.1 to 2.36.2 (#2240) Bumps `aws.sdk.version` from 2.36.1 to 2.36.2. Updates `software.amazon.awssdk:bom` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:http-client-spi` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:url-connection-client` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:s3` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:dynamodb` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:lambda` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:kinesis` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:cloudwatch` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:xray` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:sqs` from 2.32.31 to 2.36.2 Updates `software.amazon.awssdk:cloudformation` from 2.36.1 to 2.36.2 Updates `software.amazon.awssdk:sts` from 2.36.1 to 2.36.2 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.36.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.36.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.36.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.36.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index b37c468c0..3d9bb3c67 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.36.1</aws.sdk.version> + <aws.sdk.version>2.36.2</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 8a8474f0a..878826077 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.36.1</aws.sdk.version> + <aws.sdk.version>2.36.2</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 752492d6d..ae075b71d 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.36.1</aws.sdk.version> + <aws.sdk.version>2.36.2</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 2516b83f4924e786fa2d62fabfcd44c1f50965ce Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 29 Oct 2025 17:37:15 +0100 Subject: [PATCH 0933/1008] feat(idempotency): Add functional `Idempotency` API (#2244) * Decouple AspectJ dependencies on pjp from IdempotencyHandler. * Create PowertoolsIdempotency functional API with overloads for all use-cases. * Make IdempotencyConfig thread-safe to avoid Lamdba context leaks in config singleton. * Fix bug: We need to pass the return type explicitly to support deserialization to the return type of the method. * Address some pmd findings. * Make java doc consistent in PowertoolsIdempotency. * Hide checked exceptions (from AspectJ) from user-facing API in PowertoolsIdempotency. * Remove unncessary try catch Throwable in unit tests * Add E2E test for functional idempotency API. * Fix sonar issues. * Remove unncessary constructors for Input object. * Add support for generic types. * Merge PowertoolsIdempotency with Idempotency to offer a single entrypoint to idempotency. * Make ConfigHolder final. * Make ConfigHolder modifier order comply with java language spec. * Make Holder class final. --- .../handlers/idempotency-functional/pom.xml | 60 ++++ .../lambda/powertools/e2e/Function.java | 66 ++++ .../amazon/lambda/powertools/e2e/Input.java | 27 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 ++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 ++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + .../src/main/resources/log4j2.xml | 16 + .../handlers/idempotency-generics/pom.xml | 60 ++++ .../lambda/powertools/e2e/Function.java | 78 +++++ .../amazon/lambda/powertools/e2e/Input.java | 27 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 ++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 ++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + .../src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/pom.xml | 2 + .../lambda/powertools/IdempotencyE2ET.java | 31 +- .../powertools/idempotency/Idempotency.java | 238 +++++++++++++- .../idempotency/IdempotencyConfig.java | 8 +- .../internal/IdempotencyHandler.java | 28 +- .../internal/IdempotentAspect.java | 28 +- .../internal/IdempotentFunction.java | 30 ++ .../idempotency/internal/cache/LRUCache.java | 4 +- .../idempotency/IdempotencyTest.java | 311 ++++++++++++++++++ .../IdempotencyFunctionalFunction.java | 48 +++ ...IdempotencyMultiArgFunctionalFunction.java | 54 +++ .../internal/IdempotencyAspectTest.java | 71 +++- .../testutils/InMemoryPersistenceStore.java | 58 ++++ .../powertools/utilities/JsonConfig.java | 28 +- 40 files changed, 1612 insertions(+), 61 deletions(-) create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/pom.xml create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/pom.xml create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml create mode 100644 powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java 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..c8893c6e9 --- /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.5.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-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..0d14b749e --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.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.e2e; + +public class Input { + private String message; + + 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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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/idempotency-functional/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-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/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml new file mode 100644 index 000000000..5387d4059 --- /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.5.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-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..0d14b749e --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.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.e2e; + +public class Input { + private String message; + + public String getMessage() { + return message; + } + + 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..e69fa735c --- /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,61 @@ +[ + { + "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 + } +] 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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ae075b71d..7a7da9e02 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -34,6 +34,8 @@ <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> 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 c73a6d761..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 @@ -23,40 +23,43 @@ 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; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class IdempotencyE2ET { - private static Infrastructure infrastructure; - private static String functionName; + private Infrastructure infrastructure; + private String functionName; - @BeforeAll - @Timeout(value = 15, unit = TimeUnit.MINUTES) - 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 - static void tearDown() { + @AfterEach + void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } - @Test - 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\"}"; 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 index bd564caf8..4f73edea0 100644 --- 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 @@ -14,25 +14,87 @@ 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; /** - * 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> + * 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 class Idempotency { +public final class Idempotency { + private static final String DEFAULT_FUNCTION_NAME = "function"; + private IdempotencyConfig config; private BasePersistenceStore persistenceStore; @@ -81,7 +143,7 @@ private void setPersistenceStore(BasePersistenceStore persistenceStore) { this.persistenceStore = persistenceStore; } - private static class Holder { + private static final class Holder { private static final Idempotency instance = new Idempotency(); } @@ -116,5 +178,151 @@ public Config withConfig(IdempotencyConfig config) { } } + // 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/powertools-idempotency-core/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 index 9d5c66cac..0d9504483 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -25,7 +25,7 @@ /** * 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; @@ -34,7 +34,7 @@ public class IdempotencyConfig { private final boolean throwOnNoIdempotencyKey; private final String hashFunction; private final BiFunction<Object, DataRecord, Object> responseHook; - private Context lambdaContext; + private final InheritableThreadLocal<Context> lambdaContext = new InheritableThreadLocal<>(); private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, @@ -87,11 +87,11 @@ 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() { diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 0466f244f..77d3d49b0 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -21,12 +21,11 @@ import java.util.OptionalInt; import java.util.function.BiFunction; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.reflect.MethodSignature; 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; @@ -50,13 +49,21 @@ 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(); @@ -171,7 +178,6 @@ 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()); @@ -180,12 +186,12 @@ private Object handleForStatus(DataRecord record) { .getResponseHook(); final Object responseData; - if (returnType.equals(String.class)) { + 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().reader().readValue(record.getResponseData(), - returnType); + responseData = JsonConfig.get().getObjectMapper().readValue(record.getResponseData(), + returnTypeRef); } if (responseHook != null) { @@ -196,14 +202,14 @@ private Object handleForStatus(DataRecord 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/powertools-idempotency-core/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 index ea6d743f0..35c6dee40 100644 --- a/powertools-idempotency/powertools-idempotency-core/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,14 +14,21 @@ package software.amazon.lambda.powertools.idempotency.internal; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.JsonNode; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; + +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; @@ -29,11 +36,6 @@ import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; - /** * Aspect that handles the {@link Idempotent} annotation. * It uses the {@link IdempotencyHandler} to actually do the job. @@ -42,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)) { @@ -76,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/powertools-idempotency-core/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 index b57ad2977..a3bf1c5ff 100644 --- a/powertools-idempotency/powertools-idempotency-core/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/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/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/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/powertools-idempotency-core/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 index 0ccb1e5aa..384a20b2a 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -194,7 +194,7 @@ void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyException( "Test message", new RuntimeException("Test Cause"), dr)) - .when(store).saveInProgress(any(), any(), any()); + .when(store).saveInProgress(any(), any(), any()); // WHEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -538,4 +538,73 @@ 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/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-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 fc0f083e5..c6a986564 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,19 +14,23 @@ 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 io.burt.jmespath.JmesPath; import io.burt.jmespath.RuntimeConfiguration; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionRegistry; import io.burt.jmespath.jackson.JacksonRuntime; -import java.util.function.Supplier; import software.amazon.lambda.powertools.utilities.jmespath.Base64Function; import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; @@ -51,8 +55,7 @@ public final class JsonConfig { private final FunctionRegistry customFunctions = defaultFunctions.extend( new Base64Function(), new Base64GZipFunction(), - new JsonFunction() - ); + new JsonFunction()); private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() .withSilentTypeErrors(true) @@ -77,6 +80,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 * @@ -103,7 +123,7 @@ public <T extends BaseFunction> void addFunction(T function) { jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); } - private static class ConfigHolder { + private static final class ConfigHolder { private static final JsonConfig instance = new JsonConfig(); } } From dcb2b605b13cfa9391ca79795fbf7955b6cf8e74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:23:48 +0100 Subject: [PATCH 0934/1008] chore: bump aws.sdk.version from 2.36.2 to 2.37.1 (#2246) Bumps `aws.sdk.version` from 2.36.2 to 2.37.1. Updates `software.amazon.awssdk:url-connection-client` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:sdk-core` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:s3` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:kinesis` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:sqs` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.36.2 to 2.37.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.37.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.37.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index a5d14f0c1..19b404c82 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.36.2</sdk.version> + <sdk.version>2.37.1</sdk.version> </properties> <dependencies> From 74ca992a317346bd991024ad8988a117e1466f0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:24:13 +0100 Subject: [PATCH 0935/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.221.0 to 2.221.1 (#2248) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.221.0 to 2.221.1. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/v2.221.1/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.221.0...v2.221.1) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.221.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 42777cc96..ce887884c 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.5.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.221.0</cdk.version> + <cdk.version>2.221.1</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.14.0</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 046592e1c..557611a93 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.221.0</cdk.version> + <cdk.version>2.221.1</cdk.version> </properties> <dependencies> From 6f2f262acbe248b0a6e408386be82d8a85b6d393 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:24:46 +0100 Subject: [PATCH 0936/1008] chore: bump github/codeql-action from 4.31.0 to 4.31.1 (#2249) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.0 to 4.31.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4e94bd11f71e507f7f87df81788dff88d1dacbfb...5fe9434cd24fe243e33e7f3305f8a5b519b70280) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 4ca976695..632e4b0d9 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v3.29.5 + uses: github/codeql-action/upload-sarif@5fe9434cd24fe243e33e7f3305f8a5b519b70280 # v3.29.5 with: sarif_file: results.sarif From 6e4701c75da366465b25cac43ea7669aa96bf616 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:25:00 +0100 Subject: [PATCH 0937/1008] chore: bump aws.sdk.version from 2.36.2 to 2.36.3 (#2242) Bumps `aws.sdk.version` from 2.36.2 to 2.36.3. Updates `software.amazon.awssdk:bom` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:http-client-spi` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:url-connection-client` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:s3` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:dynamodb` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:lambda` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:kinesis` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:cloudwatch` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:xray` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:sqs` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:cloudformation` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:sts` from 2.36.2 to 2.36.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.36.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.36.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.36.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 3d9bb3c67..7f9bbc6fa 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.36.2</aws.sdk.version> + <aws.sdk.version>2.36.3</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 878826077..449baba05 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.0</jackson.version> - <aws.sdk.version>2.36.2</aws.sdk.version> + <aws.sdk.version>2.36.3</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7a7da9e02..86e867548 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.36.2</aws.sdk.version> + <aws.sdk.version>2.36.3</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From e1dbd95997b00704cf509cf3fe04dd7b2d6c679b Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 31 Oct 2025 11:40:40 +0100 Subject: [PATCH 0938/1008] fix(serialization): Fix Joda time serialization for AWS events. (#2252) --- powertools-serialization/pom.xml | 4 + .../powertools/utilities/JsonConfig.java | 5 +- .../utilities/EventDeserializerTest.java | 198 ++++++++++++++---- .../jmespath/Base64FunctionTest.java | 19 +- .../jmespath/Base64GZipFunctionTest.java | 30 +-- .../utilities/jmespath/JsonFunctionTest.java | 13 +- 6 files changed, 204 insertions(+), 65 deletions(-) diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 89fb2056a..1ed2ee259 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -47,6 +47,10 @@ <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 --> <dependency> 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 c6a986564..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 @@ -25,6 +25,7 @@ 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; @@ -41,11 +42,13 @@ public final class JsonConfig { // 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. - .serializationInclusion(JsonInclude.Include.NON_NULL) + .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); 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 2914bd286..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,41 +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 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) @@ -76,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"); @@ -87,21 +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 = "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)); @@ -109,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)); @@ -117,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)); @@ -125,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"); @@ -133,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") @@ -142,14 +145,14 @@ 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)"); @@ -157,14 +160,14 @@ public void testDeserializeEmptyEventAsList_shouldThrowException() { @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"); @@ -172,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)) @@ -193,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)); @@ -222,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)); @@ -230,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)); @@ -238,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); @@ -247,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); @@ -256,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"); From f543b68171cb38df9dde72b2839aeb673e3e2b3d Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 31 Oct 2025 12:47:29 +0100 Subject: [PATCH 0939/1008] fix(logging): Fix bug where correlation_id field was missing in JSON structured output (#2256) --- .../json/resolver/PowertoolsResolver.java | 20 ++++++++++++++++++- .../src/main/resources/LambdaEcsLayout.json | 14 ++++++++----- .../src/main/resources/LambdaJsonLayout.json | 4 ++++ .../PowertoolsResolverArgumentsTest.java | 6 ++++-- .../logging/logback/LambdaEcsEncoder.java | 7 +++++++ .../internal/LambdaEcsEncoderTest.java | 4 +++- .../internal/LambdaJsonEncoderTest.java | 6 ++++-- 7 files changed, 50 insertions(+), 11 deletions(-) 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 index 8ada50f49..cef5b86ee 100644 --- 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 @@ -15,6 +15,7 @@ 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; @@ -112,7 +113,7 @@ public boolean isResolvable(LogEvent logEvent) { final String samplingRate = logEvent.getContextData().getValue(PowertoolsLoggedFields.SAMPLING_RATE.getName()); try { - return (null != samplingRate && Float.parseFloat(samplingRate) > 0.f); + return null != samplingRate && Float.parseFloat(samplingRate) > 0.f; } catch (NumberFormatException nfe) { return false; } @@ -142,6 +143,22 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { } }; + 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()); @@ -214,6 +231,7 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { { 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 } diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json index 19f13f199..58b30f60e 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json @@ -45,13 +45,13 @@ "$resolver": "thread", "field": "name" }, - "cloud.provider" : "aws", - "cloud.service.name" : "lambda", - "cloud.region" : { + "cloud.provider": "aws", + "cloud.service.name": "lambda", + "cloud.region": { "$resolver": "powertools", "field": "region" }, - "cloud.account.id" : { + "cloud.account.id": { "$resolver": "powertools", "field": "account_id" }, @@ -83,7 +83,11 @@ "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation.id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } -} \ No newline at end of file +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json index 8b811ee5f..793006502 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -65,6 +65,10 @@ "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } 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 index 546d54579..573eaddbf 100644 --- 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 @@ -90,7 +90,8 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { .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("\"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\""); @@ -122,7 +123,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { .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("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { 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 index a1a7daff1..6a82d8e67 100644 --- 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 @@ -15,6 +15,7 @@ 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; @@ -72,6 +73,7 @@ public class LambdaEcsEncoder extends EncoderBase<ILoggingEvent> { 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"; @@ -156,6 +158,11 @@ private void serializeFunctionInfo(JsonSerializer serializer, String arn, Map<St 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); + } } } 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 index 74a11813b..30ede8ba8 100644 --- 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 @@ -102,7 +102,8 @@ void shouldNotLogFunctionInfo() { // 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\""); + "\"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); @@ -175,6 +176,7 @@ private void setMDC() { 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 index 97770d3d0..16bd9e92a 100644 --- 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 @@ -135,7 +135,8 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { "\"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("\"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\""); @@ -168,7 +169,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { "\"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("\"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\""); From 71c632b70a4d1ec3146102e4c2ab5e3fa0f793d0 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 3 Nov 2025 11:42:16 +0100 Subject: [PATCH 0940/1008] feat(large-messages): Add function interface for Large Messages Utility (#2257) * Add functional API for LargeMessages utility. * Add E2E tests for functional large messages. * Use format specifiers for logging in LargeMessageE2ET.java. --- .../handlers/largemessage-functional/pom.xml | 46 +++ .../lambda/powertools/e2e/Function.java | 85 ++++++ .../src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/pom.xml | 1 + .../lambda/powertools/LargeMessageE2ET.java | 62 ++-- powertools-large-messages/pom.xml | 18 +- .../largemessages/LargeMessage.java | 12 +- .../largemessages/LargeMessages.java | 156 ++++++++++ .../internal/LargeMessageAspect.java | 13 +- .../internal/LargeMessageFunction.java | 35 +++ .../internal/LargeMessageProcessor.java | 68 +++-- .../LargeMessageProcessorFactory.java | 7 +- .../largemessages/LargeMessagesTest.java | 287 ++++++++++++++++++ .../internal/LargeMessageAspectTest.java | 102 ++++--- .../test/resources/simplelogger.properties | 7 + 15 files changed, 810 insertions(+), 105 deletions(-) create mode 100644 powertools-e2e-tests/handlers/largemessage-functional/pom.xml create mode 100644 powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java create mode 100644 powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java create mode 100644 powertools-large-messages/src/test/resources/simplelogger.properties 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..4afb633f2 --- /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.5.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/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 86e867548..f171422c5 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -27,6 +27,7 @@ <modules> <module>batch</module> <module>largemessage</module> + <module>largemessage-functional</module> <module>largemessage_idempotent</module> <module>logging-log4j</module> <module>logging-logback</module> 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 74247ca2e..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 @@ -16,9 +16,10 @@ 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; @@ -40,6 +41,7 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.RetryUtils; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class LargeMessageE2ET { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageE2ET.class); @@ -55,25 +57,34 @@ 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) - 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(); @@ -81,19 +92,24 @@ 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 - static void tearDown() { + void cleanup() { if (infrastructure != null) { infrastructure.destroy(); } } @AfterEach - 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()); @@ -103,8 +119,12 @@ void reset() { } } - @Test - void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException { + @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); @@ -146,8 +166,12 @@ void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException { assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); } - @Test - void smallSQSMessage_shouldNotReadFromS3() { + @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); diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index a405941d7..bc7bbf03a 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -14,11 +14,11 @@ --> <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> @@ -41,6 +41,13 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-common</artifactId> </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>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> @@ -102,6 +109,11 @@ <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> 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/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/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/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 From f106143a2beafd24e0258fb8318cbcacdebb6e7e Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 3 Nov 2025 17:56:19 +0100 Subject: [PATCH 0941/1008] fix(idempotency): Validate payload for optimistic idempotent writes. (#2261) * Validate payload for optimistic idempotent writes. * Re-throw IdempotencyValidationException instead of wrapping in a persistence layer exception. --- .../internal/IdempotencyHandler.java | 2 + .../persistence/BasePersistenceStore.java | 45 ++++++++++----- .../persistence/BasePersistenceStoreTest.java | 56 +++++++++++++++++-- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 77d3d49b0..d4e0d2222 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -109,6 +109,8 @@ private Object processIdempotency() throws Throwable { } } 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.", diff --git a/powertools-idempotency/powertools-idempotency-core/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 index 7e93dc00c..00ddc4336 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -16,10 +16,6 @@ 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; @@ -33,8 +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 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; @@ -125,8 +128,7 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { DataRecord.Status.COMPLETED, getExpiryEpochSecond(now), responseJson, - getHashedPayload(data) - ); + getHashedPayload(data)); LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", dataRecord.getIdempotencyKey()); updateRecord(dataRecord); @@ -157,8 +159,8 @@ 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 dataRecord = new DataRecord( @@ -167,10 +169,23 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime getExpiryEpochSecond(now), null, getHashedPayload(data), - inProgressExpirationMsTimestamp - ); + inProgressExpirationMsTimestamp); LOG.debug("saving in progress record for idempotency key: {}", dataRecord.getIdempotencyKey()); - putRecord(dataRecord, now); + + 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; + } } /** @@ -188,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); @@ -255,9 +270,9 @@ private Optional<String> getHashedIdempotencyKey(JsonNode data) { private boolean isMissingIdemPotencyKey(JsonNode data) { if (data.isContainerNode()) { - Stream<JsonNode> stream = - StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.elements(), Spliterator.ORDERED), - false); + Stream<JsonNode> stream = StreamSupport.stream( + Spliterators.spliteratorUnknownSize(data.elements(), Spliterator.ORDERED), + false); return stream.allMatch(JsonNode::isNull); } return data.isNull(); diff --git a/powertools-idempotency/powertools-idempotency-core/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 index d5d45c78f..8e46de1cc 100644 --- a/powertools-idempotency/powertools-idempotency-core/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 @@ -28,6 +28,7 @@ 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; @@ -145,8 +146,8 @@ 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); } @@ -181,7 +182,7 @@ void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { assertThatThrownBy( () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) - .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + .isInstanceOf(IdempotencyItemAlreadyExistsException.class); assertThat(status).isEqualTo(-1); } @@ -243,7 +244,8 @@ void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessingExcep 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.getResponseData()) + .isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); assertThat(cachedDr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(cachedDr.getPayloadHash()).isEmpty(); } @@ -325,6 +327,52 @@ void getRecord_invalidPayload_shouldThrowValidationException() { assertThatThrownBy( () -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) + .isInstanceOf(IdempotencyValidationException.class); + } + + @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. + } + + @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); } From 68b6c026cb62785c23e88ca88f601ff4833e3f63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:08:56 +0100 Subject: [PATCH 0942/1008] chore: bump aws.sdk.version from 2.36.3 to 2.37.2 (#2253) Bumps `aws.sdk.version` from 2.36.3 to 2.37.2. Updates `software.amazon.awssdk:url-connection-client` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:sdk-core` from 2.37.1 to 2.37.2 Updates `software.amazon.awssdk:s3` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:kinesis` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:sqs` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.37.1 to 2.37.2 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 19b404c82..75b283d3d 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.37.1</sdk.version> + <sdk.version>2.37.2</sdk.version> </properties> <dependencies> From 93d9ee60b69d4de6caab6c41d1f8a7ee15b0dd7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:09:08 +0100 Subject: [PATCH 0943/1008] chore: bump com.fasterxml.jackson:jackson-bom from 2.20.0 to 2.20.1 (#2254) Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.20.0 to 2.20.1. - [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.20.0...jackson-bom-2.20.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson:jackson-bom dependency-version: 2.20.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449baba05..a790145c1 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ <maven.deploy.plugin.version>3.1.2</maven.deploy.plugin.version> <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> - <jackson.version>2.20.0</jackson.version> + <jackson.version>2.20.1</jackson.version> <aws.sdk.version>2.36.3</aws.sdk.version> <aws.xray.recorder.version>2.20.0</aws.xray.recorder.version> <payloadoffloading-common.version>2.2.0</payloadoffloading-common.version> From 25f02a9c2cd022729a0751f7bdbf4a38f8e8410d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:09:24 +0100 Subject: [PATCH 0944/1008] chore: bump github/codeql-action from 4.31.1 to 4.31.2 (#2255) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.1 to 4.31.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5fe9434cd24fe243e33e7f3305f8a5b519b70280...0499de31b99561a6d14a36a5f662c2a54f91beee) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 632e4b0d9..996135fa9 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@5fe9434cd24fe243e33e7f3305f8a5b519b70280 # v3.29.5 + uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 with: sarif_file: results.sarif From 357f1206801ce8ada3784ecf398ecc8015a722ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:10:31 +0100 Subject: [PATCH 0945/1008] chore: bump squidfunk/mkdocs-material in /docs (#2259) Bumps squidfunk/mkdocs-material from `f5c556a` to `58dee36`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 9b8bde4c5..88d6ca284 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:f5c556a6d30ce0c1c0df10e3c38c79bbcafdaea4b1c1be366809d0d4f6f9d57f +FROM squidfunk/mkdocs-material@sha256:58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 41f3ef517db2561e596963590b9beb47a2bd6b0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 11:12:25 +0100 Subject: [PATCH 0946/1008] chore: bump aws.sdk.version from 2.36.3 to 2.37.3 (#2258) Bumps `aws.sdk.version` from 2.36.3 to 2.37.3. Updates `software.amazon.awssdk:bom` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:http-client-spi` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:url-connection-client` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:s3` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:dynamodb` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:lambda` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:kinesis` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:cloudwatch` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:xray` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:sqs` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:cloudformation` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:sts` from 2.36.3 to 2.37.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.37.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.37.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 7f9bbc6fa..a164315fe 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.36.3</aws.sdk.version> + <aws.sdk.version>2.37.3</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index a790145c1..dbf1ac898 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.36.3</aws.sdk.version> + <aws.sdk.version>2.37.3</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index f171422c5..82b7a1de3 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.36.3</aws.sdk.version> + <aws.sdk.version>2.37.3</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 93582c5a895252b7c81ab1757f6048e60e44d596 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 4 Nov 2025 15:31:35 +0100 Subject: [PATCH 0947/1008] Update UA configurator to comply with 50 chars limit. (#2268) --- .../internal/UserAgentConfigurator.java | 35 +++++++- .../internal/UserAgentConfiguratorTest.java | 81 ++++++++++++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java index 7deca89f1..27b69d5ad 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java @@ -82,18 +82,29 @@ static String getVersionFromProperties(String propertyFileName, String versionKe /** * Configures the AWS SDK to use Powertools user agent by setting the sdk.ua.appId system property. - * If the property is already set and not empty, appends the Powertools user agent with a "/" separator. + * 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()) { - System.setProperty(SDK_USER_AGENT_APP_ID, existingValue + "/" + powertoolsUserAgent); + if (existingValue == null || existingValue.isEmpty()) { + newValue = powertoolsUserAgent; } else { - System.setProperty(SDK_USER_AGENT_APP_ID, powertoolsUserAgent); + 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 @@ -101,6 +112,22 @@ public static void configureUserAgent(String ptFeature) { } } + /** + * 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} 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 index fbe4529d8..33050d8b4 100644 --- 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 @@ -131,7 +131,7 @@ void testConfigureUserAgent() { } @Test - void testConfigureUserAgent_WithExistingValue() { + void testConfigureUserAgent_WithExistingUserValue() { System.setProperty("sdk.ua.appId", "UserValueABC123"); UserAgentConfigurator.configureUserAgent("test-feature"); @@ -139,6 +139,64 @@ void testConfigureUserAgent_WithExistingValue() { .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", ""); @@ -148,4 +206,25 @@ void testConfigureUserAgent_WithEmptyExistingValue() { .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"); + } + } From d1c5b46ea263ebafabecf64a0b72a2652806be35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:33:36 +0100 Subject: [PATCH 0948/1008] chore: bump org.junit.jupiter:junit-jupiter from 5.14.0 to 5.14.1 (#2265) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework) from 5.14.0 to 5.14.1. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.14.0...r5.14.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-version: 5.14.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index ce887884c..3a07e6e6c 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -9,7 +9,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.221.1</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> - <junit.version>5.14.0</junit.version> + <junit.version>5.14.1</junit.version> </properties> <build> <plugins> From 578cb8616a3a76c921ef01a319bcb601e153fb31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:34:09 +0100 Subject: [PATCH 0949/1008] chore: bump aws.sdk.version from 2.37.3 to 2.37.4 (#2264) Bumps `aws.sdk.version` from 2.37.3 to 2.37.4. Updates `software.amazon.awssdk:bom` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:http-client-spi` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:url-connection-client` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:s3` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:dynamodb` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:lambda` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:kinesis` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:cloudwatch` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:xray` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:sqs` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:cloudformation` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:sts` from 2.37.3 to 2.37.4 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.37.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.37.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index a164315fe..edd774e74 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.37.3</aws.sdk.version> + <aws.sdk.version>2.37.4</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index dbf1ac898..bc5389841 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.37.3</aws.sdk.version> + <aws.sdk.version>2.37.4</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 82b7a1de3..827feccea 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.37.3</aws.sdk.version> + <aws.sdk.version>2.37.4</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 9251e18ab11eb647d9d040c6a6822c73fbe5822f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:00:10 +0100 Subject: [PATCH 0950/1008] chore: bump io.github.ascopes:protobuf-maven-plugin (#2269) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.3.0 to 3.10.2. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.3.0...v3.10.2) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.10.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/tools/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-kafka/tools/pom.xml b/examples/powertools-examples-kafka/tools/pom.xml index 80ed6c264..e6f2654d1 100644 --- a/examples/powertools-examples-kafka/tools/pom.xml +++ b/examples/powertools-examples-kafka/tools/pom.xml @@ -62,7 +62,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.3.0</version> + <version>3.10.2</version> <executions> <execution> <goals> From 9ca99332edc2732cce3a7eeb9b2acf79aa489abf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:53:05 +0100 Subject: [PATCH 0951/1008] chore(ci): bump version to 2.6.0 (#2270) * chore(ci): bump version to 2.6.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page <pagejep@amazon.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 62 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index b53d5aeb1..58d3901dc 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 71d924c26..9ce451a33 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 75b283d3d..707b9bcfb 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index b26007d44..35184b6d2 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.5.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 ``` ### Deploy with CDK @@ -32,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-2.5.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.6.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index b577855ce..01365932e 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.5.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index edd774e74..389245bbc 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 8a5ac6601..756c082b8 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 3a07e6e6c..4d3414c63 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.221.1</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 9b89d923a..36524f0b1 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.5.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.5.0' - aspect 'software.amazon.lambda:powertools-metrics:2.5.0' + aspect 'software.amazon.lambda:powertools-tracing:2.6.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.6.0' + aspect 'software.amazon.lambda:powertools-metrics:2.6.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 85ec540a9..c3713c344 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.5.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.5.0") - aspect("software.amazon.lambda:powertools-metrics:2.5.0") + aspect("software.amazon.lambda:powertools-tracing:2.6.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.6.0") + aspect("software.amazon.lambda:powertools-metrics:2.6.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 71b93be66..b879ae49d 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 60d6b796f..a5e3881b2 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 01e734f6f..8ef3b556d 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 4b7d19fe4..d320a1d40 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 6ee70def3..3fa68c7e0 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-idempotency-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM</name> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index d84651e15..0654867f9 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index fe56c6b1e..5428655bd 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index e72df3086..bfebbe57d 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index e2ee15f26..17a1efa79 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index cdf131482..9b03e3046 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 683faf783..7120d3a91 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index b84fac5bb..5826e7456 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.5.0</version> + <version>2.6.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 62e211728..da4303c38 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -127,7 +127,7 @@ extra_javascript: extra: powertools: - version: 2.5.0 + version: 2.6.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index bc5389841..66d15731b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index edb849cea..ccf926d39 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index f6d8ad33f..015179833 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 3583a4dc0..2610a8c4f 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 21161f9e7..a630ba09b 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index c8893c6e9..73c8780d3 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-idempotency-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index 5387d4059..d89aa33e2 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-idempotency-generics</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 7c0e957fe..ea84e7b26 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index 4afb633f2..094c54841 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-largemessage-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 469a9a48a..3004a884c 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 1e89edd67..74a7b2999 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index 5d774fe21..a8f79df30 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-logging-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index dddc84152..0ed761274 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-logging-log4j</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index 4f3a28c72..d8523f721 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-logging-logback</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 84006df9a..3b60d2aba 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index e0a58f9bd..495b51311 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 827feccea..2e024375c 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 5f6943ca6..2aaae55f5 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index e9e8f5c47..c966194b1 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 21e80c20e..85b8f47dc 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 557611a93..1898e309f 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 8961701d3..7bfe38ef4 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 64907072f..0e784c0f5 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index a55947d4b..ea562ea89 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index fd001f575..ac8c95f05 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index bc7bbf03a..e29327c7d 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 2fa4680fb..bcc28c6c2 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index b4800ab54..fc47ac84d 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 0cfbb6104..2269303f5 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 74b4ca7c4..ae2871b41 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index f1e4eae57..72d3133d3 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index eefc46ac5..454102ede 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index cfc8f64d3..594761bee 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 9d3ca0500..4b4616cb2 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index a277337a7..0e31ed729 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 61fb4c3b9..b1b655fe8 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.5.0</version> + <version>2.6.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 1ed2ee259..79b6653fa 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index c1f1433a1..3b1e58ccd 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 57f8f25fb..2c119972d 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.5.0</version> + <version>2.6.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From e928caedcf5fbbb0e43fbffd38b7d9a617fd3fef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:39:04 +0100 Subject: [PATCH 0952/1008] chore: bump com.amazonaws:aws-lambda-java-runtime-interface-client (#2272) Bumps [com.amazonaws:aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs) from 2.8.6 to 2.8.7. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-runtime-interface-client dependency-version: 2.8.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 389245bbc..e04a8dc43 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -77,7 +77,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.6</version> + <version>2.8.7</version> </dependency> </dependencies> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index b879ae49d..cfd640aa1 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.6</version> + <version>2.8.7</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 3fa68c7e0..dff0082f1 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -43,7 +43,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.6</version> + <version>2.8.7</version> </dependency> <dependency> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index bfebbe57d..9c62498d5 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.6</version> + <version>2.8.7</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 9b03e3046..7df5805ce 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -36,7 +36,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.6</version> + <version>2.8.7</version> </dependency> </dependencies> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 2e024375c..95a09d1d9 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -129,7 +129,7 @@ <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-runtime-interface-client</artifactId> - <version>2.8.6</version> + <version>2.8.7</version> </dependency> </dependencies> </dependencyManagement> From f08e8731aaac85014d752c58294c9e11e89b5d03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:39:08 +0100 Subject: [PATCH 0953/1008] chore: bump aws.sdk.version from 2.37.4 to 2.37.5 (#2271) Bumps `aws.sdk.version` from 2.37.4 to 2.37.5. Updates `software.amazon.awssdk:bom` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:http-client-spi` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:url-connection-client` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:s3` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:dynamodb` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:lambda` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:kinesis` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:cloudwatch` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:xray` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:sqs` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:cloudformation` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:sts` from 2.37.4 to 2.37.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index e04a8dc43..a88339746 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.37.4</aws.sdk.version> + <aws.sdk.version>2.37.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 66d15731b..d262fbc6b 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.37.4</aws.sdk.version> + <aws.sdk.version>2.37.5</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 95a09d1d9..599588c1d 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.37.4</aws.sdk.version> + <aws.sdk.version>2.37.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 1ed3daeba384f12ec5df3ea11d42ef038c791246 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:08:51 +0100 Subject: [PATCH 0954/1008] chore: bump aws.sdk.version from 2.37.2 to 2.37.5 (#2273) Bumps `aws.sdk.version` from 2.37.2 to 2.37.5. Updates `software.amazon.awssdk:url-connection-client` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:sdk-core` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:s3` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:kinesis` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:sqs` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.37.2 to 2.37.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 707b9bcfb..103a8a4b4 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.37.2</sdk.version> + <sdk.version>2.37.5</sdk.version> </properties> <dependencies> From 6b032bbaaf019283931ec9fc83d147e9b271cd65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:09:06 +0100 Subject: [PATCH 0955/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.221.1 to 2.222.0 (#2274) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.221.1 to 2.222.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.221.1...v2.222.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.222.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 4d3414c63..9348d52d9 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.6.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.221.1</cdk.version> + <cdk.version>2.222.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.14.1</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1898e309f..09bfe8271 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.2</constructs.version> - <cdk.version>2.221.1</cdk.version> + <cdk.version>2.222.0</cdk.version> </properties> <dependencies> From e39cc6041417eb13896bc2a0b41a3c4eafd84b11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:09:18 +0100 Subject: [PATCH 0956/1008] chore: bump sam/build-java21 (#2275) Bumps sam/build-java21 from `72709a0` to `51709ae`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 51709ae612478654f833998a3455519d0524157230757cf6327e402213811e38 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 8d26b4770..1abb53643 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:72709a010ebfc993fb402c1bce599a0b754110b873d1a58e60c52136e8c8f3f1 +FROM public.ecr.aws/sam/build-java21@sha256:51709ae612478654f833998a3455519d0524157230757cf6327e402213811e38 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 691aeea73b55126072065237b8bed41b58d497ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:01:37 +0100 Subject: [PATCH 0957/1008] chore: bump aws.sdk.version from 2.37.5 to 2.38.2 (#2276) Bumps `aws.sdk.version` from 2.37.5 to 2.38.2. Updates `software.amazon.awssdk:url-connection-client` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:sdk-core` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:s3` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:kinesis` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:sqs` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.37.5 to 2.38.2 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.38.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.38.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 103a8a4b4..518cdbdd4 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.37.5</sdk.version> + <sdk.version>2.38.2</sdk.version> </properties> <dependencies> From 7c6d6ea17aad11987a276aa06ac4fc63c38e58f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:01:45 +0100 Subject: [PATCH 0958/1008] chore: bump software.constructs:constructs from 10.4.2 to 10.4.3 (#2277) Bumps [software.constructs:constructs](https://github.com/aws/constructs) from 10.4.2 to 10.4.3. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.4.2...v10.4.3) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-version: 10.4.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 09bfe8271..ddf1a360a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -30,7 +30,7 @@ <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> - <constructs.version>10.4.2</constructs.version> + <constructs.version>10.4.3</constructs.version> <cdk.version>2.222.0</cdk.version> </properties> From 598f004a0f56fad3e0e4f70594ff28fe1de4f616 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:56:26 +0100 Subject: [PATCH 0959/1008] chore: bump aws.sdk.version from 2.37.5 to 2.38.3 (#2278) Bumps `aws.sdk.version` from 2.37.5 to 2.38.3. Updates `software.amazon.awssdk:bom` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:http-client-spi` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:url-connection-client` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:s3` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:dynamodb` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:lambda` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:kinesis` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:cloudwatch` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:xray` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:sqs` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:cloudformation` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:sts` from 2.37.5 to 2.38.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.38.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.38.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index a88339746..392d08b4b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.37.5</aws.sdk.version> + <aws.sdk.version>2.38.3</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index d262fbc6b..4d5ff7248 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.37.5</aws.sdk.version> + <aws.sdk.version>2.38.3</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 599588c1d..bf2e78f8c 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.37.5</aws.sdk.version> + <aws.sdk.version>2.38.3</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 7cdac1d58edf24648bd0e9875f01748e43391f0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:56:40 +0100 Subject: [PATCH 0960/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.222.0 to 2.223.0 (#2279) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.222.0 to 2.223.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.222.0...v2.223.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.223.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 9348d52d9..4ca38877d 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.6.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.222.0</cdk.version> + <cdk.version>2.223.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.14.1</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index ddf1a360a..860593819 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.3</constructs.version> - <cdk.version>2.222.0</cdk.version> + <cdk.version>2.223.0</cdk.version> </properties> <dependencies> From d550de02ffe2ef6901db9cb35b8338f61097bfb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:57:42 +0100 Subject: [PATCH 0961/1008] chore: bump squidfunk/mkdocs-material in /docs (#2280) Bumps squidfunk/mkdocs-material from `58dee36` to `980e11f`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 88d6ca284..56c4c19ea 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441 +FROM squidfunk/mkdocs-material@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From addad65980bd89fd992f6fe91de7b70af68b5135 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:57:55 +0100 Subject: [PATCH 0962/1008] chore: bump actions/dependency-review-action from 4.8.1 to 4.8.2 (#2281) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.8.1 to 4.8.2. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/40c09b7dc99638e5ddb0bfd91c1673effc064d8a...3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 692acd64d..c6e06950f 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -26,6 +26,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents - uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: config-file: './.github/dependency-review-config.yml' From f9f227cf5a289da7c701057c9228296811ebfb3a Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 13 Nov 2025 10:19:29 +0100 Subject: [PATCH 0963/1008] docs: Document new functional API (#2282) * Initial version of documentation revamp adding functional API and usage patterns. * Update tracing docs highlighting functional API. * Update metrics docs highlighting functional API. * Update parameters docs highlithing functional approach. * Update validation docs highlithing functional API. * Update README.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/metrics.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/tracing.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/index.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/utilities/idempotency.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/utilities/idempotency.md Co-authored-by: Stefano Vozza <svozza@gmail.com> --------- Co-authored-by: Stefano Vozza <svozza@gmail.com> --- README.md | 14 +- docs/FAQs.md | 4 + docs/core/logging.md | 264 ++++++++++++++++--- docs/core/metrics.md | 82 +++--- docs/core/tracing.md | 142 +++++----- docs/index.md | 39 ++- docs/processes/maintainers.md | 2 +- docs/usage-patterns.md | 183 +++++++++++++ docs/utilities/idempotency.md | 427 +++++++++++++++++++++++++------ docs/utilities/large_messages.md | 271 +++++++++++++++----- docs/utilities/parameters.md | 44 ++-- docs/utilities/validation.md | 23 +- mkdocs.yml | 4 +- 13 files changed, 1167 insertions(+), 332 deletions(-) create mode 100644 docs/usage-patterns.md diff --git a/README.md b/README.md index 58d3901dc..e771db05c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>2.6.0</version> </dependency> <dependency> @@ -116,6 +116,7 @@ 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" } @@ -126,10 +127,10 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 11 up to 21 as well as the -[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). +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 good version of `aspectjrt` to your dependencies based on the JDK used for building your function: +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: ```xml <dependency> @@ -142,12 +143,13 @@ You may need to add the good version of `aspectjrt` to your dependencies based o <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) | - -More info [here](https://github.com/aws-powertools/powertools-lambda-java/pull/1519/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R191). +| `25` | `1.9.25` (or higher) | </details> diff --git a/docs/FAQs.md b/docs/FAQs.md index 75f699c91..cea4b774f 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -7,6 +7,8 @@ description: Frequently Asked Questions 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" @@ -31,6 +33,8 @@ To enable in-place weaving feature you need to use following `aspectj-maven-plug 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" diff --git a/docs/core/logging.md b/docs/core/logging.md index db01a3ec0..8358087d2 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -23,13 +23,12 @@ Logging provides an opinionated logger with output structured as JSON. 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"}. ### Installation -Depending on preference, you must choose to use either _log4j2_ or _logback_ as your log provider. In both cases you need to configure _aspectj_ -to weave the code and make sure the annotation is processed. +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-7 24-27" + ```xml hl_lines="3-12 30-33" <dependencies> ... <dependency> @@ -37,10 +36,16 @@ to weave the code and make sure the annotation is processed. <artifactId>powertools-logging-log4j</artifactId> <version>{{ powertools.version }}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</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> ... @@ -82,7 +87,7 @@ to weave the code and make sure the annotation is processed. === "logback" - ```xml hl_lines="3-7 24-27" + ```xml hl_lines="3-12 30-33" <dependencies> ... <dependency> @@ -90,10 +95,16 @@ to weave the code and make sure the annotation is processed. <artifactId>powertools-logging-logback</artifactId> <version>{{ powertools.version }}</version> </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</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> ... @@ -137,10 +148,10 @@ to weave the code and make sure the annotation is processed. === "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 { @@ -148,7 +159,8 @@ to weave the code and make sure the annotation is processed. } dependencies { - aspect 'software.amazon.lambda:powertools-logging-log4j:{{ 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 @@ -157,10 +169,10 @@ to weave the code and make sure the annotation is processed. === "logback" - ```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 { @@ -168,7 +180,8 @@ to weave the code and make sure the annotation is processed. } dependencies { - aspect 'software.amazon.lambda:powertools-logging-logback:{{ 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 = 11 @@ -317,9 +330,9 @@ If you set `POWERTOOLS_LOG_LEVEL` lower than ALC, we will emit a warning informi ## Basic Usage -To use Lambda Powertools for AWS Lambda Logging, use the `@Logging` annotation in your code and the standard _SLF4J_ logger: +You can use Powertools for AWS Lambda Logging with either the `@Logging` annotation or the functional API: -=== "PaymentFunction.java" +=== "@Logging annotation" ```java hl_lines="8 10 12 14" import org.slf4j.Logger; @@ -341,6 +354,30 @@ To use Lambda Powertools for AWS Lambda Logging, use the `@Logging` annotation i } ``` +=== "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> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + 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); + }); + } + } + ``` + ## Standard structured keys Your logs will always include the following keys in your structured logging: @@ -376,11 +413,10 @@ The following keys will also be added to all your structured logs (unless [confi #### Logging a correlation ID -You can set a correlation ID using the `correlationIdPath` attribute of the `@Logging`annotation, -by passing a [JMESPath expression](https://jmespath.org/tutorial.html){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). -=== "AppCorrelationIdPath.java" +=== "@Logging annotation" ```java hl_lines="5" public class AppCorrelationIdPath implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -395,6 +431,24 @@ including our custom [JMESPath Functions](../utilities/serialization.md#built-in } } ``` + +=== "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" @@ -422,7 +476,7 @@ including our custom [JMESPath Functions](../utilities/serialization.md#built-in To ease routine tasks like extracting correlation ID from popular event sources, we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions). -=== "AppCorrelationId.java" +=== "@Logging annotation" ```java hl_lines="1 7" import software.amazon.lambda.powertools.logging.CorrelationIdPaths; @@ -440,6 +494,26 @@ we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions) } ``` +=== "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); + }); + } + } + ``` + === "Example Event" ```json hl_lines="3" @@ -668,10 +742,9 @@ 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. If you want all custom keys to be deleted, you can use -`clearState=true` attribute on the `@Logging` annotation. +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. -=== "CreditCardFunction.java" +=== "@Logging annotation" ```java hl_lines="5 8" public class CreditCardFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -716,15 +789,18 @@ this means that custom keys, added with the MDC can be persisted across invocati `clearState` is based on `MDC.clear()`. State clearing is automatically done at the end of the execution of the handler if set to `true`. +???+ 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. + ## Logging incoming event -When debugging in non-production environments, you can instruct the `@Logging` annotation to log the incoming event with `logEvent` param or via `POWERTOOLS_LOGGER_LOG_EVENT` env var. +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 + This is disabled by default to prevent sensitive info being logged. -=== "AppLogEvent.java" +=== "@Logging annotation" ```java hl_lines="5" public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -738,17 +814,36 @@ When debugging in non-production environments, you can instruct the `@Logging` a } ``` +=== "Functional API" + + ```java hl_lines="1 9" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + 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); + }); + } + } + ``` + ???+ note - If you use this on a RequestStreamHandler, the SDK must duplicate input streams in order to log them. + 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. ## Logging handler response -When debugging in non-production environments, you can instruct the `@Logging` annotation to log the response with `logResponse` param or via `POWERTOOLS_LOGGER_LOG_RESPONSE` env var. +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 + This is disabled by default to prevent sensitive info being logged. -=== "AppLogResponse.java" +=== "@Logging annotation" ```java hl_lines="5" public class AppLogResponse implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -762,18 +857,41 @@ When debugging in non-production environments, you can instruct the `@Logging` a } ``` +=== "Functional API" + + ```java hl_lines="1 11" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + public class AppLogResponse implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + 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. + 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. You can instruct the `@Logging` annotation to log this kind of exception +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 + This is disabled by default to prevent double logging. -=== "AppLogResponse.java" +???+ 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> { @@ -787,6 +905,29 @@ with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. } ``` +=== "Functional API" + + ```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 ### Buffering logs @@ -1050,7 +1191,10 @@ You can manually control the log buffer using the `PowertoolsLogging` utility cl 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. -=== "PaymentFunction.java" +???+ 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> { @@ -1068,6 +1212,30 @@ Use the `@Logging` annotation to automatically flush buffered logs when an uncau } ``` +=== "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 @@ -1161,13 +1329,13 @@ sequenceDiagram ## 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 leve, using the`POWERTOOLS_LOGGER_SAMPLE_RATE` environment variable or -via `samplingRate` attribute on the `@Logging` annotation. +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 on environment variable is given precedence over sampling rate configuration on annotation, provided it's in valid value range. + Configuration via environment variable is given precedence over sampling rate configuration, provided it's in valid value range. -=== "Sampling via annotation attribute" +=== "@Logging annotation" ```java hl_lines="5" public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -1182,6 +1350,23 @@ via `samplingRate` attribute on the `@Logging` annotation. } ``` +=== "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" @@ -1198,7 +1383,7 @@ via `samplingRate` attribute on the `@Logging` annotation. ## Built-in Correlation ID expressions -You can use any of the following built-in JMESPath expressions as part of `@Logging(correlationIdPath = ...)`: +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"`**. @@ -1237,8 +1422,7 @@ The `JsonTemplateLayout` is automatically configured with the provided template: "field": "name" }, "message": { - "$resolver": "powertools", - "field": "message" + "$resolver": "message" }, "error": { "message": { @@ -1299,6 +1483,10 @@ The `JsonTemplateLayout` is automatically configured with the provided template: "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 71c56bb8b..e7f7bd87f 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -48,6 +48,7 @@ Visit the AWS documentation for a complete explanation for [Amazon CloudWatch co </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> ... @@ -89,10 +90,10 @@ Visit the AWS documentation for a complete explanation for [Amazon CloudWatch co === "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 { @@ -100,7 +101,8 @@ Visit the AWS documentation for a complete explanation for [Amazon CloudWatch co } 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 @@ -127,27 +129,12 @@ Metrics has three global settings that will be used across all metrics emitted. 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-metrics-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. -=== "template.yaml" - - ```yaml hl_lines="9 10" - Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java11 - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: payment - POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline - ``` - -=== "MetricsEnabledHandler.java" +=== "@FlushMetrics annotation" ```java hl_lines="9" import software.amazon.lambda.powertools.metrics.FlushMetrics; @@ -165,9 +152,45 @@ For most use-cases, we recommend using Environment variables and only overwrite } ``` -`Metrics` is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. To guarantee that metrics are flushed properly the `@FlushMetrics` annotation must be added on the lambda handler. +=== "MetricsBuilder" + + ```java hl_lines="7-8" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsBuilder; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsBuilder.builder() + .withNamespace("ServerlessAirline") + .withService("payment") + .build(); + + @Override + public Object handleRequest(Object input, Context context) { + // ... + metrics.flush(); + } + } + ``` + +=== "Environment variables" + + ```yaml hl_lines="9 10" + Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + ... + 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 "You can use the Metrics utility without the `@FlushMetrics` annotation and flush manually. Read more in the [advanced section below](#usage-without-metrics-annotation)." +!!!info "Read more about the functional approach in the [advanced section below](#usage-without-flushmetrics-annotation)." ## Creating metrics @@ -381,7 +404,7 @@ You can use `addMetadata` for advanced use cases, where you want to add metadata This will not be available during metrics visualization, use Dimensions for this purpose. !!! info - Adding metadata with a key that is the same as an existing metric will be ignored + Adding metadata with a key that is the same as an existing metric will be ignored. <!-- prettier-ignore-end --> === "App.java" @@ -468,7 +491,7 @@ You can create metrics with different configurations e.g. different namespace an === "App.java" - ```java hl_lines="12-18" + ```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; @@ -504,22 +527,22 @@ You can create metrics with different configurations e.g. different namespace an ### Usage without `@FlushMetrics` annotation -The `Metrics` Singleton provides all configuration options via `MetricsBuilder` in addition to the `@FlushMetrics` annotation. This can be useful if work in an environment or framework that does not leverage the vanilla Lambda `handleRequest` method. +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. Note that it is necessary to manually flush metrics now. +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-12 19 23" + ```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 without annotation + // Create and configure a Metrics singleton using the functional approach private static final Metrics metrics = MetricsBuilder.builder() .withNamespace("ServerlessAirline") .withRaiseOnEmptyMetrics(true) @@ -533,8 +556,9 @@ The following example shows how to configure a custom `Metrics` Singleton using // Dimensions are also optional. metrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); - // Add metrics to the custom metrics singleton + // Add metrics metrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + // Manually flush metrics metrics.flush(); } } diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 8129d45ba..95fbe6d06 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -20,7 +20,7 @@ a provides functionality to reduce the overhead of performing common tracing tas === "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 25-28" <dependencies> ... <dependency> @@ -32,6 +32,7 @@ a provides functionality to reduce the overhead of performing common tracing tas </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> ... @@ -73,10 +74,10 @@ a provides functionality to reduce the overhead of performing common tracing tas === "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 { @@ -84,11 +85,12 @@ a provides functionality to reduce the overhead of performing common tracing tas } 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 = 11 - targetCompatibility = 11 + sourceCompatibility = 11 // or higher + targetCompatibility = 11 // or higher ``` ## Initialization @@ -118,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. -=== "Tracing annotation" +**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" ```java hl_lines="3 10 15" public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { @@ -146,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" @@ -157,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> { @@ -183,7 +209,7 @@ different supported `captureMode` to record response, exception or both. } ``` -=== "Disable Globally" +=== "@Tracing annotation - Disable Globally" ```yaml hl_lines="11 12" Resources: @@ -200,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 @@ -272,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; @@ -317,25 +338,33 @@ under a subsegment, or you are doing multithreaded programming. Refer examples b ## Instrumenting SDK clients and HTTP calls -Powertools for Lambda (Java) cannot intercept SDK clients instantiation to add X-Ray instrumentation. You should make sure to instrument the SDK clients explicitly. Refer details on -[how to instrument SDK client with Xray](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-awssdkclients) -and [outgoing http calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-httpclients). For example: +### AWS SDK for Java 2.x -=== "LambdaHandler.java" +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. - ```java hl_lines="1 2 7" - import com.amazonaws.xray.AWSXRay; - import com.amazonaws.xray.handlers.TracingHandler; +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 AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() - .withRegion(Regions.fromName(System.getenv("AWS_REGION"))) - .withRequestHandlers(new TracingHandler(AWSXRay.getGlobalRecorder())) + 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 When using `@Tracing` annotation, your Junit test cases needs to be configured to create parent Segment required by [AWS X-Ray SDK for Java](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html). @@ -351,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> @@ -370,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" @@ -418,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 9c5c803cb..655c16e03 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,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). @@ -71,8 +72,8 @@ 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/) @@ -90,7 +91,7 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> + <artifactId>powertools-logging-log4j</artifactId> <version>{{ powertools.version }}</version> </dependency> <dependency> @@ -107,7 +108,8 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo ... </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> ... @@ -175,7 +177,8 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo } 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 }}' } @@ -184,28 +187,15 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo targetCompatibility = 11 ``` -???+ 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+. +???+ 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). ### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 11 up to 21 as well as the -[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). +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 following modules, Powertools for AWS Lambda (Java) leverages the **aspectj** library to provide annotations: -- Logging -- Metrics -- Tracing -- Parameters -- Idempotency -- Validation -- Large messages +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. - -You may need to add the good version of `aspectjrt` to your dependencies based on the jdk used for building your function: +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: ```xml <dependency> @@ -215,17 +205,18 @@ You may need to add the good version of `aspectjrt` to your dependencies based o </dependency> ``` -Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md) between this library and the JDK: +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.** + Explicit parameters take precedence over environment variables. | Environment variable | Description | Utility | | -------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------- | diff --git a/docs/processes/maintainers.md b/docs/processes/maintainers.md index 8f7f6a8fd..f2839c532 100644 --- a/docs/processes/maintainers.md +++ b/docs/processes/maintainers.md @@ -17,7 +17,6 @@ This is document explains who the maintainers are, their responsibilities, and h | Maintainer | GitHub ID | Affiliation | | --------------- | -------------------------------------------------------------------- | ----------- | | Philipp Page | [phipag](https://github.com/phipag){target="\_blank" rel="nofollow"} | Amazon | -| Simon Thulbourn | [sthulb](https://github.com/sthulb){target="\_blank" rel="nofollow"} | Amazon | ## Emeritus @@ -25,6 +24,7 @@ Previous active maintainers who contributed to this project. | 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 | 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/idempotency.md b/docs/utilities/idempotency.md index 83f256e6b..cecc65d7b 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -29,7 +29,7 @@ times with the same parameters**. This makes idempotent operations safe to retry === "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 16 18 25-28" <dependencies> ... <dependency> @@ -41,6 +41,7 @@ times with the same parameters**. This makes idempotent operations safe to retry </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> ... @@ -82,10 +83,10 @@ times with the same parameters**. This makes idempotent operations safe to retry === "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 { @@ -93,7 +94,8 @@ times with the same parameters**. This makes idempotent operations safe to retry } dependencies { - aspect 'software.amazon.lambda:powertools-idempotency-dynamodb:{{ 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 @@ -104,7 +106,7 @@ times with the same parameters**. This makes idempotent operations safe to retry 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** @@ -148,29 +150,29 @@ Resources: ``` !!! 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")) @@ -191,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 @@ -200,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> { + + private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler; - public AppSqsEvent() { + public SqsBatchHandler() { Idempotency.config() .withPersistenceStore( DynamoDBPersistenceStore.builder() @@ -226,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; } } ``` @@ -304,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() { @@ -344,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" @@ -417,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("TABLE_NAME")) - .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 @@ -499,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 @@ -553,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") @@ -579,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") @@ -667,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() { @@ -700,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" @@ -745,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() @@ -767,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" @@ -977,7 +1250,7 @@ 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> @@ -1046,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; @@ -1143,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() { @@ -1174,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 @@ -1201,7 +1474,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "env.json" - ```json hl_lines="3" + ```json { "IdempotentFunction": { "TABLE_NAME": "idempotency" diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index 38228afe9..9d14c8228 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -4,7 +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). +offloaded to S3 if they are larger than the maximum allowed size (1 MB). ## Features @@ -27,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 --> [*] } @@ -72,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 @@ -87,16 +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 === "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 25-28" <dependencies> ... <dependency> @@ -108,6 +106,7 @@ of amazon-sns-java-extended-client-lib. </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> ... @@ -152,7 +151,7 @@ of amazon-sns-java-extended-client-lib. ```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 { @@ -160,7 +159,7 @@ of amazon-sns-java-extended-client-lib. } 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 @@ -175,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; @@ -204,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; @@ -224,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. @@ -233,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) { @@ -243,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 @@ -317,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; @@ -342,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` diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index beb460aa6..6de47df68 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -49,6 +49,7 @@ Note that you must provide the concrete parameters module you want to use below </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> ... @@ -91,10 +92,10 @@ Note that you must provide the concrete parameters module you want to use below === "Gradle" - ```groovy hl_lines="3 11 12" + ```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 { @@ -103,7 +104,8 @@ Note that you must provide the concrete parameters module you want to use below dependencies { // TODO! Provide the parameters module you want to use here - aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' + 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 @@ -124,19 +126,18 @@ This utility requires additional permissions to work as expected. See the table | AppConfig | `AppConfigProvider.get(String)` `AppConfigProvider.getMultiple(string)` | `appconfig:StartConfigurationSession`, `appConfig:GetLatestConfiguration` | ## Retrieving Parameters -You can retrieve parameters either using annotations or by using the `xParamProvider` class for each parameter -provider directly. The latter is useful if you need to configure the underlying SDK client, for example to use -a different region or credentials, the former is simpler to use. +You can retrieve parameters using either annotations or provider classes directly: + +- **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 ## Built-in provider classes -This section describes the built-in provider classes for each parameter store, providing -examples showing how to inject parameters using annotations, and how to use the provider -interface. In cases where a provider supports extra features, these will also be described. +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. ### Secrets Manager -=== "Secrets Manager: @SecretsParam" +=== "@SecretsParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -156,7 +157,7 @@ interface. In cases where a provider supports extra features, these will also be } ``` -=== "Secrets Manager: SecretsProvider" +=== "SecretsProvider class" ```java hl_lines="12-15 19" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; @@ -195,7 +196,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **recursive()** | `False` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | -=== "SSM Parameter Store: @SSMParam" +=== "@SSMParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -214,7 +215,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "SSM Parameter Store: SSMProvider" +=== "SSMProvider class" ```java hl_lines="12-15 19-20 22" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; @@ -246,15 +247,14 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "SSM Parameter Store: Additional Options" +=== "Additional Options" - ```java hl_lines="9 12" - import software.amazon.lambda.powertools.parameters.SSMProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + ```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"); @@ -267,7 +267,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen ### DynamoDB -=== "DynamoDB: @DyanmoDbParam" +=== "@DynamoDbParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -286,7 +286,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "DynamoDB: DynamoDbProvider" +=== "DynamoDbProvider class" ```java hl_lines="12-15 19-20 22" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; @@ -320,7 +320,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen ### AppConfig -=== "AppConfig: @AppConfigParam" +=== "@AppConfigParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -339,7 +339,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "AppConfig: AppConfigProvider" +=== "AppConfigProvider class" ```java hl_lines="12-15 19-20" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index ec35b7034..8e0d2c631 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -26,6 +26,7 @@ This utility provides JSON Schema validation for payloads held within events and </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> ... @@ -67,10 +68,10 @@ This utility provides JSON Schema validation for payloads held within events and === "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 with ValidationUtils.validate() } repositories { @@ -78,7 +79,8 @@ This utility provides JSON Schema validation for payloads held within events and } 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 @@ -87,17 +89,18 @@ This utility provides JSON Schema validation for payloads held within events and ## Validating events -You can validate inbound and outbound events using `@Validation` annotation. +You can validate inbound and outbound events using either the `@Validation` annotation or the functional approach with `ValidationUtils.validate()` methods: -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** - 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 -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)). +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)). The validator is configured to enable format assertions by default even for 2019-09 and 2020-12. ### Validation annotation -`@Validation` annotation is used to validate either inbound events or functions' response. +The `@Validation` annotation is used to validate either inbound events or functions' response. 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. @@ -129,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" diff --git a/mkdocs.yml b/mkdocs.yml index da4303c38..3914cfa1a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,9 +1,10 @@ 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/latest/ +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 @@ -99,6 +100,7 @@ plugins: sections: Project Overview: - index.md + - usage-patterns.md - changelog.md - FAQs.md - roadmap.md From c2763c5a7db3ad63472d54b128d2a1dc74deb810 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 13 Nov 2025 10:35:58 +0100 Subject: [PATCH 0964/1008] chore(ci): Enable Java 25 builds. (#2285) --- .github/workflows/check-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index e1d949aad..88f1eabee 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -47,6 +47,7 @@ on: - 'powertools-large-messages/**' - 'powertools-logging/**' - 'powertools-metrics/**' + - 'powertools-kafka/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - 'powertools-sqs/**' @@ -72,6 +73,7 @@ jobs: - 11 - 17 - 21 + - 25 steps: - id: checkout name: Checkout repository From 464490dd6e08c01b3e92507a05a34b555730fdaa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:42:28 +0100 Subject: [PATCH 0965/1008] chore(ci): bump version to 2.7.0 (#2286) * chore(ci): bump version to 2.7.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page <pagejep@amazon.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 62 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index e771db05c..6d3689092 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging-log4j</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 9ce451a33..c02d318cc 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 518cdbdd4..5226166fb 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 35184b6d2..84d9d7fac 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 ``` ### Deploy with CDK @@ -32,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-2.6.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.7.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index 01365932e..85c8b386f 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 392d08b4b..74ced3a4f 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 756c082b8..dbd6743a8 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 4ca38877d..87dc26169 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.223.0</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 36524f0b1..0b7951f11 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.6.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.6.0' - aspect 'software.amazon.lambda:powertools-metrics:2.6.0' + aspect 'software.amazon.lambda:powertools-tracing:2.7.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.7.0' + aspect 'software.amazon.lambda:powertools-metrics:2.7.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index c3713c344..47e82c5b8 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.6.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.6.0") - aspect("software.amazon.lambda:powertools-metrics:2.6.0") + aspect("software.amazon.lambda:powertools-tracing:2.7.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.7.0") + aspect("software.amazon.lambda:powertools-metrics:2.7.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index cfd640aa1..682e1a10b 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index a5e3881b2..d03cafcfb 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 8ef3b556d..996f77d4b 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index d320a1d40..d28835a8a 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index dff0082f1..205958c9a 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-idempotency-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM</name> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index 0654867f9..694b79342 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 5428655bd..184025be8 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 9c62498d5..a83ddddb8 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 17a1efa79..83efef4d2 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 7df5805ce..b77ce975e 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 7120d3a91..789d9969f 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 5826e7456..abcdd4e3a 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.6.0</version> + <version>2.7.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 3914cfa1a..98f6945d7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -129,7 +129,7 @@ extra_javascript: extra: powertools: - version: 2.6.0 + version: 2.7.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 4d5ff7248..48be39d47 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index ccf926d39..bce58ab58 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 015179833..a3270563a 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 2610a8c4f..d4e9f6213 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index a630ba09b..2c726340a 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index 73c8780d3..133a0ccff 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-idempotency-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index d89aa33e2..a69babd0d 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-idempotency-generics</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index ea84e7b26..cbe7e0cac 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index 094c54841..e2e67b2da 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-largemessage-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 3004a884c..56d179c3b 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 74a7b2999..9896db217 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index a8f79df30..ba532c3db 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-logging-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index 0ed761274..445da94e2 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-logging-log4j</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index d8523f721..9f5035722 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-logging-logback</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 3b60d2aba..5bf3bd5ef 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 495b51311..e30a51150 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index bf2e78f8c..afc21131f 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 2aaae55f5..1a3b56a77 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index c966194b1..d2e1266fc 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 85b8f47dc..6832280a6 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 860593819..998c6a803 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7bfe38ef4..f119ca445 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 0e784c0f5..5e57ee136 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index ea562ea89..25f6f77c7 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index ac8c95f05..a07d4e0d6 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index e29327c7d..6353cb089 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index bcc28c6c2..9fd7b1e62 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index fc47ac84d..8006baa7d 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 2269303f5..70bbbdfc4 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index ae2871b41..1ea59493c 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 72d3133d3..158fdc978 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 454102ede..9d9adc16b 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 594761bee..d66153da6 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 4b4616cb2..f126716d6 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 0e31ed729..e6bf1ea27 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index b1b655fe8..b40046bde 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.6.0</version> + <version>2.7.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 79b6653fa..d7f33df28 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 3b1e58ccd..68231dbe1 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 2c119972d..74051989e 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.6.0</version> + <version>2.7.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 286a2d06d510e0799511b1c20340071b86979295 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Thu, 13 Nov 2025 17:22:33 +0100 Subject: [PATCH 0966/1008] =?UTF-8?q?improv(parameters):=20Make=20paramete?= =?UTF-8?q?rs=20top-level=20provider=20thread-safe=20fo=E2=80=A6=20(#2284)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improv(parameters): Make parameters top-level provider thread-safe for cache and transformer management. * Use countdown latch instead of Thread.sleep for transformation manager tests. * Make SSMProvider and AppConfigProvider thread-safe. * Make all tests package-private. * Fix sonar finding AZp41z3q7Kei0U644EOS. * Fix sonar finding AZp41z3q7Kei0U644EOS for real now. * Fix PMD findings. * Fix PMD findings. * Fix PMD findings. --- .../appconfig/AppConfigProvider.java | 4 +- .../appconfig/AppConfigParamAspectTest.java | 4 +- .../dynamodb/DynamoDbParamAspectTest.java | 4 +- .../dynamodb/DynamoDbProviderE2ETest.java | 26 ++-- .../dynamodb/DynamoDbProviderTest.java | 44 +++--- .../secrets/SecretsParamAspectTest.java | 4 +- .../secrets/SecretsProviderTest.java | 14 +- .../parameters/ssm/SSMProvider.java | 30 ++-- .../parameters/ssm/SSMParamAspectTest.java | 4 +- .../parameters/ssm/SSMProviderTest.java | 132 ++++++++++++++--- .../parameters/BaseProviderTest.java | 62 ++++---- .../ParamProvidersIntegrationTest.java | 8 +- .../parameters/cache/CacheManagerTest.java | 110 ++++++++++++-- .../parameters/cache/DataStoreTest.java | 14 +- .../transform/Base64TransformerTest.java | 6 +- .../transform/JsonTransformerTest.java | 10 +- .../transform/TransformationManagerTest.java | 140 ++++++++++++++++-- .../powertools/parameters/BaseProvider.java | 31 ++-- .../parameters/cache/CacheManager.java | 23 ++- .../parameters/cache/DataStore.java | 13 +- .../transform/TransformationManager.java | 43 ++++-- 21 files changed, 535 insertions(+), 191 deletions(-) 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 index 37f07ae7a..06d00ffbe 100644 --- 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 @@ -14,8 +14,8 @@ package software.amazon.lambda.powertools.parameters.appconfig; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; @@ -46,7 +46,7 @@ public class AppConfigProvider extends BaseProvider { private final AppConfigDataClient client; private final String application; private final String environment; - private final Map<String, EstablishedSession> establishedSessions = new HashMap<>(); + private final Map<String, EstablishedSession> establishedSessions = new ConcurrentHashMap<>(); AppConfigProvider(CacheManager cacheManager, TransformationManager transformationManager, AppConfigDataClient client, String environment, String application) { 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 index df3191632..a32cc20a5 100644 --- 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 @@ -22,10 +22,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class AppConfigParamAspectTest { +class AppConfigParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked AppConfigProvider String environment = "myEnvironment"; String appName = "myApp"; 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 index 07e93a7c1..4294eca48 100644 --- 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 @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class DynamoDbParamAspectTest { +class DynamoDbParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked DynamoDbProvider String tableName = "my-test-tablename"; String key = "myKey"; diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java index 2695938d8..af2617edf 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java @@ -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 diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 68d48b01c..64f29db79 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -45,9 +45,9 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @ExtendWith(MockitoExtension.class) -public class DynamoDbProviderTest { +class DynamoDbProviderTest { - private final String tableName = "ddb-test-table"; + private static final String TABLE_NAME = "ddb-test-table"; @Mock DynamoDbClient client; @@ -64,19 +64,19 @@ public class DynamoDbProviderTest { private DynamoDbProvider provider; @BeforeEach - public void init() { + void init() { openMocks(this); CacheManager cacheManager = new CacheManager(); - provider = new DynamoDbProvider(cacheManager, transformationManager, 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() @@ -89,12 +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) @@ -108,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<>()) @@ -122,10 +122,10 @@ 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() @@ -138,7 +138,7 @@ public void getValueWithMalformedRowThrows() { } @Test - public void getValues() { + void getValues() { // Arrange String key = "Key1"; @@ -146,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)); @@ -166,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()); @@ -185,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() @@ -204,10 +204,10 @@ public void getMultipleValuesMissingSortKey_throwsException() { } @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")); @@ -224,7 +224,7 @@ public void getValuesWithMalformedRowThrows() { } @Test - public void testDynamoDBBuilderMissingTable_throwsException() { + void testDynamoDBBuilderMissingTable_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() @@ -233,7 +233,7 @@ public void testDynamoDBBuilderMissingTable_throwsException() { } @Test - public void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { + void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { // Act DynamoDbProvider dynamoDbProvider = DynamoDbProvider.builder().withTable("test-table") 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 index 7aa0f0872..5523cbb0e 100644 --- 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 @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class SecretsParamAspectTest { +class SecretsParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked SecretsProvider String key = "myKey"; String value = "mySecretValue"; diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java index d0b32874a..a6fbe1c8e 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java @@ -40,7 +40,7 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @ExtendWith(MockitoExtension.class) -public class SecretsProviderTest { +class SecretsProviderTest { @Mock SecretsManagerClient client; @@ -56,13 +56,13 @@ public class SecretsProviderTest { SecretsProvider provider; @BeforeEach - public void init() { + void init() { cacheManager = new CacheManager(); 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(); @@ -76,7 +76,7 @@ public void getValue() { } @Test - public void getValueBase64() { + void getValueBase64() { String key = "Key2"; String expectedValue = "Value2"; byte[] valueb64 = Base64.getEncoder().encode(expectedValue.getBytes()); @@ -91,14 +91,14 @@ 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 testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { + void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); @@ -109,7 +109,7 @@ public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() } @Test - public void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java index c24b08b84..3cf728219 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java @@ -66,8 +66,8 @@ 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/> @@ -109,7 +109,7 @@ public static SSMProvider create() { public String getValue(String key) { GetParameterRequest request = GetParameterRequest.builder() .name(key) - .withDecryption(decrypt) + .withDecryption(decrypt.get()) .build(); return client.getParameter(request).parameter().value(); } @@ -122,7 +122,7 @@ public String getValue(String key) { * @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; } @@ -133,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; } @@ -160,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(); @@ -170,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 contain 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)) { @@ -196,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 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 index e56d20ffa..abfc1d7fa 100644 --- 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 @@ -21,13 +21,13 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class SSMParamAspectTest { +class SSMParamAspectTest { // This class tests the SSM Param aspect in the same fashion // as the tests for the aspects for the other providers. @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { String key = "myKey"; String value = "mySecretValue"; diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index db45dc21c..fb475a737 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -23,6 +23,8 @@ 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; @@ -32,6 +34,7 @@ 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; @@ -41,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; @@ -60,14 +63,14 @@ public class SSMProviderTest { SSMProvider provider; @BeforeEach - public void init() { + void init() { MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); provider = new SSMProvider(cacheManager, null, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; initMock(expectedValue); @@ -80,7 +83,7 @@ public void getValue() { } @Test - public void getValueDecrypted() { + void getValueDecrypted() { String key = "Key2"; String expectedValue = "Value2"; initMock(expectedValue); @@ -93,7 +96,7 @@ 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()); @@ -116,7 +119,7 @@ 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()); @@ -139,7 +142,7 @@ 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()); @@ -161,12 +164,12 @@ public void getMultiple_cached_shouldNotCallSSM() { } @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()); @@ -185,25 +188,118 @@ 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 testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act SSMProvider ssmProvider = SSMProvider.builder() .build(); // Assert - assertDoesNotThrow(()->ssmProvider.withTransformation(json)); + 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) { diff --git a/powertools-parameters/powertools-parameters-tests/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 index cbc8f5b30..dd31ce016 100644 --- a/powertools-parameters/powertools-parameters-tests/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 @@ -38,7 +38,7 @@ 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; @@ -47,7 +47,7 @@ public class BaseProviderTest { boolean getFromStore = false; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); transformationManager = new TransformationManager(); @@ -55,7 +55,7 @@ public void setup() { } @Test - public void get_notCached_shouldGetValue() { + void get_notCached_shouldGetValue() { String foo = provider.get("toto"); assertThat(foo).isEqualTo("valueFromStore"); @@ -63,7 +63,7 @@ public void get_notCached_shouldGetValue() { } @Test - public void get_cached_shouldGetFromCache() { + void get_cached_shouldGetFromCache() { provider.get("foo"); getFromStore = false; @@ -73,7 +73,7 @@ public void get_cached_shouldGetFromCache() { } @Test - public void get_expired_shouldGetValue() { + void get_expired_shouldGetValue() { provider.get("bar"); getFromStore = false; @@ -84,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"); @@ -92,7 +92,7 @@ public void getMultiple_notCached_shouldGetValue() { } @Test - public void getMultiple_cached_shouldGetFromCache() { + void getMultiple_cached_shouldGetFromCache() { provider.getMultiple("foo"); getFromStore = false; @@ -102,7 +102,7 @@ public void getMultiple_cached_shouldGetFromCache() { } @Test - public void getMultiple_expired_shouldGetValue() { + void getMultiple_expired_shouldGetValue() { provider.getMultiple("bar"); getFromStore = false; @@ -113,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; @@ -124,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; @@ -135,7 +135,7 @@ public void get_customTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTL_cached_shouldGetFromCache() { + void get_customDefaultTTL_cached_shouldGetFromCache() { provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); provider.get("foobar"); getFromStore = false; @@ -147,7 +147,7 @@ public void get_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTL_expired_shouldGetValue() { + void get_customDefaultTTL_expired_shouldGetValue() { provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); getFromStore = false; @@ -158,7 +158,7 @@ public void get_customDefaultTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.get("foobaz"); getFromStore = false; @@ -169,7 +169,7 @@ public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { + void get_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); @@ -183,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"); @@ -192,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); 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); @@ -215,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); @@ -227,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); @@ -240,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) @@ -255,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) @@ -270,7 +270,7 @@ 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.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); @@ -286,7 +286,7 @@ 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.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); @@ -302,7 +302,7 @@ 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.cacheManager.setDefaultExpirationTime(Duration.ofSeconds(5)); @@ -319,7 +319,7 @@ 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.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); @@ -336,7 +336,7 @@ public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_noTransformationManager_shouldThrowException() { + void get_noTransformationManager_shouldThrowException() { provider = new BasicProvider(new CacheManager(), null); assertThatIllegalStateException() @@ -344,14 +344,14 @@ public void get_noTransformationManager_shouldThrowException() { } @Test - public void getObject_noTransformationManager_shouldThrowException() { + 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"); @@ -365,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"); @@ -384,7 +384,7 @@ public BasicProvider(CacheManager cacheManager, TransformationManager transforma super(cacheManager, transformationManager); } - public void setValue(String value) { + void setValue(String value) { this.value = value; } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java index 6b3cf7641..5bc609777 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java @@ -46,7 +46,7 @@ import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; @ExtendWith(MockitoExtension.class) -public class ParamProvidersIntegrationTest { +class ParamProvidersIntegrationTest { @Mock SsmClient ssmClient; @@ -66,7 +66,7 @@ public class ParamProvidersIntegrationTest { ArgumentCaptor<GetSecretValueRequest> secretsCaptor; @Test - public void ssmProvider_get() { + void ssmProvider_get() { SSMProvider ssmProvider = SSMProvider.builder() .withClient(ssmClient) .build(); @@ -84,7 +84,7 @@ public void ssmProvider_get() { } @Test - public void ssmProvider_getMultiple() { + void ssmProvider_getMultiple() { SSMProvider ssmProvider = SSMProvider.builder() .withClient(ssmClient) .build(); @@ -111,7 +111,7 @@ public void ssmProvider_getMultiple() { } @Test - public void secretsProvider_get() { + void secretsProvider_get() { SecretsProvider secretsProvider = SecretsProvider.builder() .withClient(secretsManagerClient) .build(); 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 index 2bcfcc566..d22572fad 100644 --- 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 @@ -21,23 +21,24 @@ import java.time.Clock; import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class CacheManagerTest { +class CacheManagerTest { CacheManager manager; Clock clock; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); manager = new CacheManager(); } @Test - public void getIfNotExpired_notExpired_shouldReturnValue() { + void getIfNotExpired_notExpired_shouldReturnValue() { manager.putInCache("key", "value"); Optional<String> value = manager.getIfNotExpired("key", clock.instant()); @@ -46,7 +47,7 @@ public void getIfNotExpired_notExpired_shouldReturnValue() { } @Test - public void getIfNotExpired_expired_shouldReturnNothing() { + void getIfNotExpired_expired_shouldReturnNothing() { manager.putInCache("key", "value"); Optional<String> value = manager.getIfNotExpired("key", offset(clock, of(6, SECONDS)).instant()); @@ -55,7 +56,7 @@ public void getIfNotExpired_expired_shouldReturnNothing() { } @Test - public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { + void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { manager.setExpirationTime(of(42, SECONDS)); manager.putInCache("key", "value"); @@ -65,18 +66,17 @@ public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValu } @Test - public void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { + 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() { + void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.setExpirationTime(of(2, SECONDS)); manager.putInCache("key", "value"); @@ -87,7 +87,7 @@ public void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_sho } @Test - public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { + void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.setExpirationTime(of(2, SECONDS)); manager.putInCache("key", "value"); @@ -101,4 +101,96 @@ public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() 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/powertools-parameters-tests/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 index e86ded9be..0de31e63d 100644 --- a/powertools-parameters/powertools-parameters-tests/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/powertools-parameters-tests/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 index ea713b552..8dbddacf6 100644 --- a/powertools-parameters/powertools-parameters-tests/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/powertools-parameters-tests/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 index 5cb980cc7..48cebb6b0 100644 --- a/powertools-parameters/powertools-parameters-tests/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/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 index 39e69f9e0..fad6f5391 100644 --- 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 @@ -21,41 +21,44 @@ 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; -public class TransformationManagerTest { +class TransformationManagerTest { TransformationManager manager; Class<BasicTransformer> basicTransformer = BasicTransformer.class; @BeforeEach - public void setup() { + void setup() { manager = new TransformationManager(); } @Test - public void setTransformer_shouldTransform() { + void setTransformer_shouldTransform() { manager.setTransformer(json); assertThat(manager.shouldTransform()).isTrue(); } @Test - public void notSetTransformer_shouldNotTransform() { + void notSetTransformer_shouldNotTransform() { assertThat(manager.shouldTransform()).isFalse(); } @Test - public void performBasicTransformation_noTransformer_shouldThrowException() { + void performBasicTransformation_noTransformer_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> manager.performBasicTransformation("value")); } @Test - public void performBasicTransformation_notBasicTransformer_shouldThrowException() { + void performBasicTransformation_notBasicTransformer_shouldThrowException() { manager.setTransformer(json); assertThatIllegalStateException() @@ -63,7 +66,7 @@ public void performBasicTransformation_notBasicTransformer_shouldThrowException( } @Test - public void performBasicTransformation_abstractTransformer_throwsTransformationException() { + void performBasicTransformation_abstractTransformer_throwsTransformationException() { manager.setTransformer(basicTransformer); assertThatExceptionOfType(TransformationException.class) @@ -71,7 +74,7 @@ public void performBasicTransformation_abstractTransformer_throwsTransformationE } @Test - public void performBasicTransformation_shouldPerformTransformation() { + void performBasicTransformation_shouldPerformTransformation() { manager.setTransformer(base64); String expectedValue = "bar"; @@ -81,27 +84,136 @@ public void performBasicTransformation_shouldPerformTransformation() { } @Test - public void performComplexTransformation_noTransformer_shouldThrowException() { + void performComplexTransformation_noTransformer_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); } @Test - public void performComplexTransformation_shouldPerformTransformation() { + void performComplexTransformation_shouldPerformTransformation() { manager.setTransformer(json); - ObjectToDeserialize object = - manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", - ObjectToDeserialize.class); + ObjectToDeserialize object = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(object).isNotNull(); } @Test - public void performComplexTransformation_throwsTransformationException() { + 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/BaseProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java index bedace28c..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,8 +29,20 @@ /** * 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"; @@ -91,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( @@ -110,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); @@ -143,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; @@ -175,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) { @@ -207,7 +220,7 @@ protected Instant now() { protected void resetToDefaults() { cacheManager.resetExpirationTime(); if (transformationManager != null) { - transformationManager.setTransformer(null); + transformationManager.unsetTransformer(); } } 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/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); } } From 03ca72b27463e5848f582c689b474cdb92643f61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:27:22 +0100 Subject: [PATCH 0967/1008] chore: bump aws.sdk.version from 2.38.3 to 2.38.5 (#2287) Bumps `aws.sdk.version` from 2.38.3 to 2.38.5. Updates `software.amazon.awssdk:bom` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:http-client-spi` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:url-connection-client` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:s3` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:dynamodb` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:lambda` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:kinesis` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:cloudwatch` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:xray` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:sqs` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:cloudformation` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:sts` from 2.38.3 to 2.38.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.38.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.38.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 74ced3a4f..824b98da9 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.38.3</aws.sdk.version> + <aws.sdk.version>2.38.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 48be39d47..536ba6002 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.38.3</aws.sdk.version> + <aws.sdk.version>2.38.5</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index afc21131f..7838dfc03 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.38.3</aws.sdk.version> + <aws.sdk.version>2.38.5</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 4e83d8a3b5cfbdb26d6ae5db81393d9c47c78df5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:27:30 +0100 Subject: [PATCH 0968/1008] chore: bump commons-io:commons-io from 2.20.0 to 2.21.0 (#2288) Bumps [commons-io:commons-io](https://github.com/apache/commons-io) from 2.20.0 to 2.21.0. - [Changelog](https://github.com/apache/commons-io/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-io/compare/rel/commons-io-2.20.0...rel/commons-io-2.21.0) --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-version: 2.21.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 998c6a803..6dd94910a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -95,7 +95,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.20.0</version> + <version>2.21.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> From 3ed0e0dea3f5c5988dbc8e34ccfb7ffb207f6133 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 17 Nov 2025 10:56:22 +0100 Subject: [PATCH 0969/1008] improv(metrics): Add thread-safety to Metrics utility through request-scoped Metrics instance management (#2294) * Add proxy to manage thread local metrics provider instances to achieve zero-lock thread-safety. * Refactor from inheritable thread local to tracking by trace id. Similar to log buffer. * Do not hard code JVM system property for trace id. * Fix PMD findings. --- .../powertools/metrics/MetricsFactory.java | 18 +- .../internal/RequestScopedMetricsProxy.java | 156 ++++++++++ .../metrics/ConfigurationPrecedenceTest.java | 13 +- .../metrics/MetricsBuilderTest.java | 18 +- .../metrics/MetricsFactoryTest.java | 70 ++++- .../metrics/RequestHandlerTest.java | 2 +- .../internal/EmfMetricsLoggerTest.java | 14 +- .../internal/LambdaMetricsAspectTest.java | 8 +- .../RequestScopedMetricsProxyTest.java | 277 ++++++++++++++++++ 9 files changed, 536 insertions(+), 40 deletions(-) create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java 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 index 67ab17b7b..0644329c9 100644 --- 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 @@ -16,9 +16,11 @@ 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; @@ -28,7 +30,7 @@ */ public final class MetricsFactory implements Resource { private static MetricsProvider provider = new EmfMetricsProvider(); - private static Metrics metrics; + private static RequestScopedMetricsProxy metricsProxy; // Dummy instance to register MetricsFactory with CRaC private static final MetricsFactory INSTANCE = new MetricsFactory(); @@ -44,23 +46,23 @@ public final class MetricsFactory implements Resource { * @return the singleton Metrics instance */ public static synchronized Metrics getMetricsInstance() { - if (metrics == null) { - metrics = provider.getMetricsInstance(); + if (metricsProxy == null) { + metricsProxy = new RequestScopedMetricsProxy(provider); // Apply default configuration from environment variables String envNamespace = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); if (envNamespace != null) { - metrics.setNamespace(envNamespace); + 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)) { - metrics.setDefaultDimensions(DimensionSet.of("Service", serviceName)); + metricsProxy.setDefaultDimensions(DimensionSet.of("Service", serviceName)); } } - return metrics; + return metricsProxy; } /** @@ -73,8 +75,8 @@ public static synchronized void setMetricsProvider(MetricsProvider metricsProvid throw new IllegalArgumentException("Metrics provider cannot be null"); } provider = metricsProvider; - // Reset the metrics instance so it will be recreated with the new provider - metrics = null; + // Reset the metrics proxy so it will be recreated with the new provider + metricsProxy = null; } @Override 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/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java index c9c772313..492ecfc1e 100644 --- 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 @@ -33,8 +33,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.metrics.model.MetricUnit; 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: @@ -44,7 +44,7 @@ */ class ConfigurationPrecedenceTest { - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -65,10 +65,10 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); @@ -183,7 +183,7 @@ void shouldUseDefaultsWhenNoConfiguration() throws Exception { assertThat(rootNode.has("Service")).isFalse(); } - private static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + 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) { @@ -193,7 +193,8 @@ public String handleRequest(Map<String, Object> input, Context context) { } } - private static class HandlerWithDefaultMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + private static final class HandlerWithDefaultMetricsAnnotation + implements RequestHandler<Map<String, Object>, String> { @Override @FlushMetrics public String handleRequest(Map<String, Object> input, Context context) { 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 index bd300fb6b..12ac46e43 100644 --- 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 @@ -27,15 +27,15 @@ 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.TestMetrics; import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; class MetricsBuilderTest { - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -46,10 +46,10 @@ void setUp() { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); @@ -151,7 +151,7 @@ void shouldBuildWithMultipleDefaultDimensions() throws Exception { } @Test - void shouldBuildWithCustomMetricsProvider() { + void shouldBuildWithCustomMetricsProvider() throws Exception { // Given MetricsProvider testProvider = new TestMetricsProvider(); @@ -161,7 +161,13 @@ void shouldBuildWithCustomMetricsProvider() { .build(); // Then - assertThat(metrics).isInstanceOf(TestMetrics.class); + 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 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 index 4fc98d2a5..c9b183a1a 100644 --- 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 @@ -29,10 +29,11 @@ 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.TestMetrics; import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; class MetricsFactoryTest { @@ -40,7 +41,7 @@ class MetricsFactoryTest { private static final String TEST_NAMESPACE = "TestNamespace"; private static final String TEST_SERVICE = "TestService"; - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -61,10 +62,11 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); @@ -126,7 +128,7 @@ void shouldUseServiceNameFromEnvironmentVariable() throws Exception { } @Test - void shouldSetCustomMetricsProvider() { + void shouldSetCustomMetricsProvider() throws Exception { // Given MetricsProvider testProvider = new TestMetricsProvider(); @@ -135,7 +137,13 @@ void shouldSetCustomMetricsProvider() { Metrics metrics = MetricsFactory.getMetricsInstance(); // Then - assertThat(metrics).isInstanceOf(TestMetrics.class); + 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 @@ -163,4 +171,54 @@ void shouldNotSetServiceDimensionWhenServiceUndefined() throws Exception { // 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/RequestHandlerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java index d3ed64fe3..fcd677c02 100644 --- 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 @@ -56,7 +56,7 @@ void tearDown() throws Exception { System.setOut(STDOUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); 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 index bab039640..c2238711a 100644 --- 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 @@ -38,14 +38,15 @@ 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.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.common.stubs.TestLambdaContext; class EmfMetricsLoggerTest { @@ -66,19 +67,14 @@ void setUp() throws Exception { coldStartField.setAccessible(true); coldStartField.set(null, null); - metrics = MetricsFactory.getMetricsInstance(); + metrics = new EmfMetricsLogger(new EnvironmentProvider(), new MetricsContext()); metrics.setNamespace("TestNamespace"); System.setOut(new PrintStream(outputStreamCaptor)); } @AfterEach - void tearDown() throws Exception { + void tearDown() { System.setOut(standardOut); - - // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); - field.setAccessible(true); - field.set(null, null); } @ParameterizedTest 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 031fe4553..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 @@ -41,7 +41,7 @@ class LambdaMetricsAspectTest { - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -62,10 +62,10 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); } @@ -216,7 +216,7 @@ void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exceptio JsonNode dimensions = coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); boolean hasFunctionName = false; for (JsonNode dimension : dimensions) { - if (dimension.asText().equals("FunctionName")) { + if ("FunctionName".equals(dimension.asText())) { hasFunctionName = true; break; } 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(); + } +} From 0af0ba445821cdd810da4eef2fb3de8ef0770c77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:58:59 +0100 Subject: [PATCH 0970/1008] chore: bump aws.sdk.version from 2.38.5 to 2.38.6 (#2290) Bumps `aws.sdk.version` from 2.38.5 to 2.38.6. Updates `software.amazon.awssdk:bom` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:http-client-spi` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:url-connection-client` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:s3` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:dynamodb` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:lambda` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:kinesis` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:cloudwatch` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:xray` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:sqs` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:cloudformation` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:sts` from 2.38.5 to 2.38.6 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.38.6 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.38.6 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.6 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 824b98da9..9ca4e8dc2 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.38.5</aws.sdk.version> + <aws.sdk.version>2.38.6</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 536ba6002..ba206feae 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.38.5</aws.sdk.version> + <aws.sdk.version>2.38.6</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7838dfc03..11eedca85 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.38.5</aws.sdk.version> + <aws.sdk.version>2.38.6</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From f7e3b1bddf79e9031cb8e258a05d4272255638e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:59:16 +0100 Subject: [PATCH 0971/1008] chore: bump org.apache.kafka:kafka-clients from 4.1.0 to 4.1.1 (#2291) Bumps org.apache.kafka:kafka-clients from 4.1.0 to 4.1.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-version: 4.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 184025be8..24856f80d 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -24,7 +24,7 @@ <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> - <version>4.1.0</version> <!-- Supports >= 3.0.0 --> + <version>4.1.1</version> <!-- Supports >= 3.0.0 --> </dependency> <dependency> <groupId>org.apache.avro</groupId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index a07d4e0d6..0a76f0d2e 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -34,7 +34,7 @@ </description> <properties> - <kafka-clients.version>4.1.0</kafka-clients.version> + <kafka-clients.version>4.1.1</kafka-clients.version> <avro.version>1.12.1</avro.version> <protobuf.version>4.33.0</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> From 4966668537b189e7eb2f10bace1c63516807ffab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:59:39 +0100 Subject: [PATCH 0972/1008] chore: bump sam/build-java21 (#2292) Bumps sam/build-java21 from `51709ae` to `ced5388`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: ced538830c78a0f45f91abe5f7963cb73005ad933aee7dd1160bd77d9fd3c4f3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 1abb53643..7d47b5331 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:51709ae612478654f833998a3455519d0524157230757cf6327e402213811e38 +FROM public.ecr.aws/sam/build-java21@sha256:ced538830c78a0f45f91abe5f7963cb73005ad933aee7dd1160bd77d9fd3c4f3 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 14572398c67e2b7e4405aa51ea4bf547cc54b42d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:59:57 +0100 Subject: [PATCH 0973/1008] chore: bump github/codeql-action from 4.31.2 to 4.31.3 (#2293) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.2 to 4.31.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/0499de31b99561a6d14a36a5f662c2a54f91beee...014f16e7ab1402f30e7c3329d33797e7948572db) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 996135fa9..bbf9490c4 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 + uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v3.29.5 with: sarif_file: results.sarif From f449a570236ed51ddb7f35f7a35835c5a66b26bf Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 18 Nov 2025 18:00:51 +0100 Subject: [PATCH 0974/1008] chore(ci): Enable Java 25 E2E tests on Java 25 Lambda runtime and upgrade to GraalVM 25. (#2298) * chore(ci): Enable Java 25 E2E tests on Java 25 Lambda runtime. * Only run for GraalVM 25. * Retain log groups according to retention policy of E2E tests to allow for easier debugging. * Add unsafeAllocated: true to allow native calls from Lambda runtime. --- .github/workflows/check-e2e.yml | 9 ++++----- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 16 +++++++++++++--- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- powertools-e2e-tests/handlers/pom.xml | 9 +++++++++ .../reflect-config.json | 3 ++- .../powertools/testutils/Infrastructure.java | 4 +++- .../powertools/testutils/JavaRuntime.java | 3 ++- .../src/test/resources/docker/Dockerfile | 8 ++++---- 19 files changed, 109 insertions(+), 39 deletions(-) diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index a3c4a7542..54e7cd023 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -48,12 +48,13 @@ jobs: environment: E2E strategy: fail-fast: false - max-parallel: 3 + max-parallel: 4 matrix: java: - 11 - 17 - 21 + - 25 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -81,12 +82,10 @@ jobs: environment: E2E strategy: fail-fast: false - max-parallel: 3 + max-parallel: 1 matrix: java: - - 11 - - 17 - - 21 + - 25 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 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 index 10152cc64..e97baa294 100644 --- 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 @@ -27,8 +27,18 @@ "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 + "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 } -] \ 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 index 10152cc64..e97baa294 100644 --- 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 @@ -27,8 +27,18 @@ "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 + "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 } -] \ 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 index 10152cc64..e97baa294 100644 --- 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 @@ -27,8 +27,18 @@ "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 + "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 } -] \ 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 index 10152cc64..e97baa294 100644 --- 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 @@ -27,8 +27,18 @@ "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 + "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 } -] \ 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 index 8d3f375f2..7d38fc57d 100644 --- 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 @@ -27,9 +27,19 @@ "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 + "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", 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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "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/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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 11eedca85..ef1d3e65f 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -261,6 +261,15 @@ <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> </project> 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 index e69fa735c..467af67a0 100644 --- 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 @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] 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 ea5ac3342..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 @@ -247,7 +247,7 @@ private Stack createStackWithLambda() { .create(e2eStack, functionName + "-logs") .logGroupName("/aws/lambda/" + functionName) .retention(RetentionDays.ONE_DAY) - .removalPolicy(RemovalPolicy.DESTROY) + .removalPolicy(RemovalPolicy.RETAIN) .build(); if (!StringUtils.isEmpty(idempotencyTable)) { @@ -522,6 +522,8 @@ private JavaRuntime mapRuntimeVersion(String environmentVariableName) { 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); } 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 53d35e86d..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 @@ -19,7 +19,8 @@ public enum JavaRuntime { JAVA11("java11", Runtime.JAVA_11, "11"), JAVA17("java17", Runtime.JAVA_17, "17"), - JAVA21("java21", Runtime.JAVA_21, "21"); + 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/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 7d47b5331..5d374eaa8 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,9 +1,9 @@ -# Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:ced538830c78a0f45f91abe5f7963cb73005ad933aee7dd1160bd77d9fd3c4f3 +# Use the official AWS SAM base image for Java 25 +FROM public.ecr.aws/sam/build-java25@sha256:2ef9e5b950cc79489691be16c7edff904bf196955633dc7fbbc282a1ea421ba8 # Install GraalVM dependencies -RUN curl -4 -L 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 +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 From 044b96d149816f52a247be42d929241380bfd7ba Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Fri, 21 Nov 2025 10:37:45 +0100 Subject: [PATCH 0975/1008] improv(batch): Propagate trace entity to worker threads during parallel batch processing. (#2300) --- docs/utilities/batch.md | 82 +++++++++++++++++- .../handler/DynamoDbBatchMessageHandler.java | 54 ++++++++---- .../KinesisStreamsBatchMessageHandler.java | 60 +++++++++----- .../batch/handler/SqsBatchMessageHandler.java | 70 ++++++++++------ .../internal/XRayTraceEntityPropagator.java | 83 +++++++++++++++++++ 5 files changed, 288 insertions(+), 61 deletions(-) create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 3f9b6e53d..b535a90f6 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -484,7 +484,9 @@ used with SQS FIFO. In that case, an `UnsupportedOperationException` is thrown. 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! + 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" @@ -536,6 +538,84 @@ used with SQS FIFO. In that case, an `UnsupportedOperationException` is thrown. } ``` +=== "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 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 df7179a88..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,21 +14,25 @@ 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. @@ -43,8 +47,8 @@ public class DynamoDbBatchMessageHandler implements BatchMessageHandler<Dynamodb 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; @@ -65,14 +69,23 @@ public StreamsEventResponse processBatch(DynamodbEvent event, Context context) { @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 -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); - multiThreadMDC.removeThread(Thread.currentThread().getName()); - return failureOpt; + 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) @@ -84,21 +97,29 @@ public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context @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(() -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); - failureOpt.ifPresent(batchItemFailures::add); - multiThreadMDC.removeThread(Thread.currentThread().getName()); + 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) { + private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem( + DynamodbEvent.DynamodbStreamRecord streamRecord, Context context) { try { LOGGER.debug("Processing item {}", streamRecord.getEventID()); @@ -124,7 +145,8 @@ private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem(Dynamod LOGGER.warn("failureHandler threw handling failure", e2); } } - return Optional.of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(sequenceNumber).build()); + return Optional + .of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(sequenceNumber).build()); } } } 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 233830462..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,22 +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; /** @@ -49,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; @@ -76,14 +79,23 @@ public StreamsEventResponse processBatch(KinesisEvent event, Context context) { @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 -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); - multiThreadMDC.removeThread(Thread.currentThread().getName()); - return failureOpt; + 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) @@ -95,21 +107,29 @@ public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context c @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(() -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional<StreamsEventResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); - failureOpt.ifPresent(batchItemFailures::add); - multiThreadMDC.removeThread(Thread.currentThread().getName()); + 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) { + private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem( + KinesisEvent.KinesisEventRecord eventRecord, Context context) { try { LOGGER.debug("Processing item {}", eventRecord.getEventID()); @@ -141,8 +161,8 @@ private Optional<StreamsEventResponse.BatchItemFailure> processBatchItem(Kinesis } } - return Optional.of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(eventRecord.getKinesis().getSequenceNumber()).build()); + 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 ccb6a6dd7..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,24 +14,26 @@ 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.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 com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; 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; /** @@ -54,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; @@ -77,16 +79,16 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { 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; + String messageGroupId = message.getAttributes() != null ? message.getAttributes().get(MESSAGE_GROUP_ID_KEY) + : null; processBatchItem(message, context).ifPresent(batchItemFailure -> { response.getBatchItemFailures().add(batchItemFailure); if (messageGroupId != null) { 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()); + "A message in a batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too", + messageGroupId, message.getMessageId()); } }); } @@ -105,18 +107,28 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { @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"); + 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 -> { - - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional<SQSBatchResponse.BatchItemFailure> failureOpt = processBatchItem(sqsMessage, context); - multiThreadMDC.removeThread(Thread.currentThread().getName()); - return failureOpt; + 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) @@ -128,17 +140,26 @@ public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) @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"); + 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(() -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional<SQSBatchResponse.BatchItemFailure> failureOpt = processBatchItem(eventRecord, context); - failureOpt.ifPresent(batchItemFailures::add); - multiThreadMDC.removeThread(Thread.currentThread().getName()); + 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); @@ -182,6 +203,7 @@ private Optional<SQSBatchResponse.BatchItemFailure> processBatchItem(SQSEvent.SQ } private boolean isFIFOEnabled(SQSEvent sqsEvent) { - return !sqsEvent.getRecords().isEmpty() && sqsEvent.getRecords().get(0).getAttributes().get(MESSAGE_GROUP_ID_KEY) != null; + 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/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(); + } + } +} From 39c26fe3db8bc7c8869e7e1c0900fdb16d03915b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:38:12 +0100 Subject: [PATCH 0976/1008] chore: bump io.github.ascopes:protobuf-maven-plugin (#2296) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.10.2 to 3.10.3. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.10.2...v3.10.3) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.10.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 24856f80d..1e7450679 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.10.2</version> + <version>3.10.3</version> <executions> <execution> <goals> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 0a76f0d2e..0d7a4c13b 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -200,7 +200,7 @@ <plugin> <groupId>io.github.ascopes</groupId> <artifactId>protobuf-maven-plugin</artifactId> - <version>3.10.2</version> + <version>3.10.3</version> <executions> <execution> <id>generate-test-sources</id> From 3575cbfa1377480ea212cefd84d7f75755c8565c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:39:11 +0100 Subject: [PATCH 0977/1008] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.223.0 to 2.224.0 (#2297) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.223.0 to 2.224.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.223.0...v2.224.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.224.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 87dc26169..35e07bc62 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ <version>2.7.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <cdk.version>2.223.0</cdk.version> + <cdk.version>2.224.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.14.1</junit.version> </properties> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 6dd94910a..0fd169845 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <constructs.version>10.4.3</constructs.version> - <cdk.version>2.223.0</cdk.version> + <cdk.version>2.224.0</cdk.version> </properties> <dependencies> From 0c0401f6ca9ca15e5bf75a46c151081ab009a459 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:39:33 +0100 Subject: [PATCH 0978/1008] chore: bump github/codeql-action from 4.31.3 to 4.31.4 (#2301) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.3 to 4.31.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/014f16e7ab1402f30e7c3329d33797e7948572db...e12f0178983d466f2f6028f5cc7a6d786fd97f4b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index bbf9490c4..205cb0ddc 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v3.29.5 + uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v3.29.5 with: sarif_file: results.sarif From 5b3f0f583f46033edf5512d1b4472993f01fee6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:39:52 +0100 Subject: [PATCH 0979/1008] chore: bump aws.sdk.version from 2.38.2 to 2.38.7 (#2295) Bumps `aws.sdk.version` from 2.38.2 to 2.38.7. Updates `software.amazon.awssdk:url-connection-client` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:sdk-core` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:s3` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:kinesis` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:sqs` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.38.2 to 2.38.7 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.38.7 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.7 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.38.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 5226166fb..ec075cf87 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.38.2</sdk.version> + <sdk.version>2.38.7</sdk.version> </properties> <dependencies> From e523a7eb42ddc7c7353f777a47a9167f7f46f803 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:27:48 +0100 Subject: [PATCH 0980/1008] chore(ci): bump version to 2.8.0 (#2303) * chore(ci): bump version to 2.8.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page <pagejep@amazon.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 62 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 6d3689092..2644c47ab 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging-log4j</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index c02d318cc..511cdd5a9 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index ec075cf87..6d941a31f 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 84d9d7fac..b9e58eb82 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 ``` ### Deploy with CDK @@ -32,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-2.7.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.8.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index 85c8b386f..c2a9e2ffd 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 9ca4e8dc2..8dde0a034 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index dbd6743a8..29acef5e2 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 35e07bc62..7daf3153f 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.224.0</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 0b7951f11..6841d20ab 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.7.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.7.0' - aspect 'software.amazon.lambda:powertools-metrics:2.7.0' + aspect 'software.amazon.lambda:powertools-tracing:2.8.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.8.0' + aspect 'software.amazon.lambda:powertools-metrics:2.8.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 47e82c5b8..bc11eb32e 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.7.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.7.0") - aspect("software.amazon.lambda:powertools-metrics:2.7.0") + aspect("software.amazon.lambda:powertools-tracing:2.8.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.8.0") + aspect("software.amazon.lambda:powertools-metrics:2.8.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 682e1a10b..fceb4a322 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index d03cafcfb..b8606a227 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 996f77d4b..d77b0ccd6 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index d28835a8a..14c0e223e 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 205958c9a..d1c0205ff 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-idempotency-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM</name> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index 694b79342..b90705c67 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 1e7450679..f4b75722c 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index a83ddddb8..6d0c3f4a2 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-parameters-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 83efef4d2..15d68ffcd 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index b77ce975e..44661fb71 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-serialization-sam-graalvm</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM</name> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 789d9969f..93c60a2cb 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index abcdd4e3a..1337bc2ee 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.7.0</version> + <version>2.8.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 98f6945d7..5f35b96c0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -129,7 +129,7 @@ extra_javascript: extra: powertools: - version: 2.7.0 + version: 2.8.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index ba206feae..ed1d2aff9 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index bce58ab58..07d11d41c 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index a3270563a..09e3299fd 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index d4e9f6213..f14783b5b 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 2c726340a..f8e0a037f 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index 133a0ccff..2b53dc275 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-idempotency-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index a69babd0d..76e65edb1 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-idempotency-generics</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index cbe7e0cac..36181ea01 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index e2e67b2da..d29017074 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-largemessage-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 56d179c3b..53b1e7c10 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 9896db217..9f6f23389 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index ba532c3db..4616af233 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-logging-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index 445da94e2..bba711163 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-logging-log4j</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index 9f5035722..bd442fe88 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-logging-logback</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 5bf3bd5ef..90704c6d9 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index e30a51150..670eeb9c0 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ef1d3e65f..f06db02f0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <packaging>pom</packaging> <name>Handlers for End-to-End tests</name> <description>Fake handlers that use Powertools for AWS Lambda (Java).</description> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 1a3b56a77..89891df8c 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index d2e1266fc..8e5e4dadb 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 6832280a6..97e967c3b 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0fd169845..484e3b4c2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index f119ca445..d15d3445d 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 5e57ee136..801664b4a 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 25f6f77c7..a9cf5739b 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 0d7a4c13b..ad564897f 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 6353cb089..d72877552 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 9fd7b1e62..ae5788291 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 8006baa7d..1cf3bf265 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 70bbbdfc4..b5a50573d 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 1ea59493c..4da0c0a9f 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 158fdc978..a2c4a65dc 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 9d9adc16b..ab8ae0e67 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index d66153da6..76a7b8845 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index f126716d6..fbe4905be 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index e6bf1ea27..c9de1db51 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index b40046bde..3bcd4d604 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.7.0</version> + <version>2.8.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index d7f33df28..c1eea4df7 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 68231dbe1..ed2596059 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 74051989e..37063cf6d 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.7.0</version> + <version>2.8.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From dfca97e5f1ebcc3cd293ea9f386246b88caec04d Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 24 Nov 2025 13:38:29 +0100 Subject: [PATCH 0981/1008] chore(ci): Remove branch protection workflow. (#2311) --- .github/branch_protection_settings/1.x.x.json | 53 -------------- .github/branch_protection_settings/main.json | 63 ---------------- .github/branch_protection_settings/v2.json | 63 ---------------- .../workflows/security-branch-protections.yml | 72 ------------------- 4 files changed, 251 deletions(-) delete mode 100644 .github/branch_protection_settings/1.x.x.json delete mode 100644 .github/branch_protection_settings/main.json delete mode 100644 .github/branch_protection_settings/v2.json delete mode 100644 .github/workflows/security-branch-protections.yml diff --git a/.github/branch_protection_settings/1.x.x.json b/.github/branch_protection_settings/1.x.x.json deleted file mode 100644 index e52aba745..000000000 --- a/.github/branch_protection_settings/1.x.x.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection", - "required_status_checks": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_status_checks", - "strict": true, - "contexts": [ - "SonarCloud" - ], - "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_status_checks/contexts", - "checks": [ - { - "context": "SonarCloud", - "app_id": null - } - ] - }, - "required_pull_request_reviews": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_pull_request_reviews", - "dismiss_stale_reviews": false, - "require_code_owner_reviews": false, - "require_last_push_approval": false, - "required_approving_review_count": 0 - }, - "required_signatures": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_signatures", - "enabled": false - }, - "enforce_admins": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/enforce_admins", - "enabled": true - }, - "required_linear_history": { - "enabled": false - }, - "allow_force_pushes": { - "enabled": false - }, - "allow_deletions": { - "enabled": false - }, - "block_creations": { - "enabled": false - }, - "required_conversation_resolution": { - "enabled": false - }, - "lock_branch": { - "enabled": false - }, - "allow_fork_syncing": { - "enabled": false - } -} diff --git a/.github/branch_protection_settings/main.json b/.github/branch_protection_settings/main.json deleted file mode 100644 index 8ca32bb45..000000000 --- a/.github/branch_protection_settings/main.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection", - "required_status_checks": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks", - "strict": true, - "contexts": [], - "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks/contexts", - "checks": [] - }, - "restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/teams", - "apps_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/apps", - "users": [], - "teams": [], - "apps": [] - }, - "required_pull_request_reviews": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_pull_request_reviews", - "dismiss_stale_reviews": true, - "require_code_owner_reviews": true, - "require_last_push_approval": true, - "required_approving_review_count": 1, - "dismissal_restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions/teams", - "users": [], - "teams": [], - "apps": [] - } - }, - "required_signatures": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_signatures", - "enabled": false - }, - "enforce_admins": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/enforce_admins", - "enabled": true - }, - "required_linear_history": { - "enabled": true - }, - "allow_force_pushes": { - "enabled": false - }, - "allow_deletions": { - "enabled": false - }, - "block_creations": { - "enabled": true - }, - "required_conversation_resolution": { - "enabled": true - }, - "lock_branch": { - "enabled": false - }, - "allow_fork_syncing": { - "enabled": false - } -} diff --git a/.github/branch_protection_settings/v2.json b/.github/branch_protection_settings/v2.json deleted file mode 100644 index fb9fdebcd..000000000 --- a/.github/branch_protection_settings/v2.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection", - "required_status_checks": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_status_checks", - "strict": true, - "contexts": [], - "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_status_checks/contexts", - "checks": [] - }, - "restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/teams", - "apps_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/apps", - "users": [], - "teams": [], - "apps": [] - }, - "required_pull_request_reviews": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_pull_request_reviews", - "dismiss_stale_reviews": true, - "require_code_owner_reviews": false, - "require_last_push_approval": true, - "required_approving_review_count": 1, - "dismissal_restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions/teams", - "users": [], - "teams": [], - "apps": [] - } - }, - "required_signatures": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_signatures", - "enabled": false - }, - "enforce_admins": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/enforce_admins", - "enabled": false - }, - "required_linear_history": { - "enabled": true - }, - "allow_force_pushes": { - "enabled": false - }, - "allow_deletions": { - "enabled": false - }, - "block_creations": { - "enabled": true - }, - "required_conversation_resolution": { - "enabled": true - }, - "lock_branch": { - "enabled": false - }, - "allow_fork_syncing": { - "enabled": false - } -} diff --git a/.github/workflows/security-branch-protections.yml b/.github/workflows/security-branch-protections.yml deleted file mode 100644 index af6477802..000000000 --- a/.github/workflows/security-branch-protections.yml +++ /dev/null @@ -1,72 +0,0 @@ -# Branch Protections -# -# Description: -# This workflow compares current security branch protections against those stored, -# if there's any changes, it'll fail the job and alert using a Slack webhook -# -# Triggers: -# - pull_request -# - branch_protection_rule -# - cron: daily at 16:40 -# -# Secrets: -# - SECURITY.BRANCH_PROTECTION_TOKEN -# - SECURITY.SLACK_WEBHOOK_URL -# -# Notes: -# Modified copy of: https://github.com/github/docs/blob/main/.github/workflows/alert-changed-branch-protections.yml - -on: - branch_protection_rule: - schedule: - - cron: '20 16 * * *' # Run daily at 16:20 UTC - pull_request: - paths: - - .github/workflows/security-branch-protections.yml - - .github/branch_protection_settings/*.json - -name: Alert Changed Branch Protections -run-name: Alert Changed Branch Protections - -permissions: - contents: read - -jobs: - check-branch-protections: - runs-on: ubuntu-latest - permissions: - contents: write - environment: Security - if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' }} - strategy: - matrix: - # List of branches we want to monitor for protection changes - branch: - - main - - v1 - steps: - - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Fetch branch protections - id: fetch - env: - GH_TOKEN: ${{ secrets.BRANCH_PROTECTION_TOKEN }} - run: | - # Fetch branch protections and store them in a file - gh api /repos/${{ github.repository }}/branches/${{ matrix.branch }}/protection | jq \ - > .github/branch_protection_settings/${{ matrix.branch }}.json - - name: Compare branch protections - id: compare - run: | - git diff --quiet .github/branch_protection_settings/${{ matrix.branch }}.json \ - || echo "diff_failed=true" >> $GITHUB_ENV - - name: Send webhook - if: ${{ env.diff_failed == 'true' }} - run: | - curl -X POST -d '{"message": "Branch protections have changed for ${{ github.repository }} on ${{ matrix.branch }}. Please review the changes or revert the changes in GitHub. https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' \ - ${{ secrets.SLACK_WEBHOOK_URL }} - - name: Fail workflow - if: ${{ env.diff_failed == 'true' }} - run: | - git diff .github/branch_protection_settings/${{ matrix.branch }}.json - echo "::error::Branch protections have been changed" \ No newline at end of file From 8208272fb8839eba312e27c1ee5a73a466ae7800 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:44:16 +0100 Subject: [PATCH 0982/1008] chore: bump org.wiremock:wiremock from 3.13.1 to 3.13.2 (#2306) Bumps [org.wiremock:wiremock](https://github.com/wiremock/wiremock) from 3.13.1 to 3.13.2. - [Release notes](https://github.com/wiremock/wiremock/releases) - [Commits](https://github.com/wiremock/wiremock/compare/3.13.1...3.13.2) --- updated-dependencies: - dependency-name: org.wiremock:wiremock dependency-version: 3.13.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed1d2aff9..64b6e0e0d 100644 --- a/pom.xml +++ b/pom.xml @@ -389,7 +389,7 @@ <dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock</artifactId> - <version>3.13.1</version> + <version>3.13.2</version> <scope>test</scope> </dependency> </dependencies> From df4a757bdaaad2dd90093e95416a3ac8a435e30b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:44:53 +0100 Subject: [PATCH 0983/1008] chore: bump aws.sdk.version from 2.38.6 to 2.39.1 (#2305) Bumps `aws.sdk.version` from 2.38.6 to 2.39.1. Updates `software.amazon.awssdk:bom` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:http-client-spi` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:url-connection-client` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:s3` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:dynamodb` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:lambda` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:kinesis` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:cloudwatch` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:xray` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:sqs` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:cloudformation` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:sts` from 2.38.6 to 2.39.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.39.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.39.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.39.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8dde0a034..09adbfa18 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.38.6</aws.sdk.version> + <aws.sdk.version>2.39.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index 64b6e0e0d..dc2decfd6 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.38.6</aws.sdk.version> + <aws.sdk.version>2.39.1</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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index f06db02f0..a9d3c3ab5 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.38.6</aws.sdk.version> + <aws.sdk.version>2.39.1</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> <maven.deploy.skip>true</maven.deploy.skip> </properties> From 51eeaf47431f6a7d93bc3bd67942849595ebbf37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:45:08 +0100 Subject: [PATCH 0984/1008] chore: bump org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0 (#2304) Bumps org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc2decfd6..1e4c3a9fd 100644 --- a/pom.xml +++ b/pom.xml @@ -310,7 +310,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.19.0</version> + <version>3.20.0</version> </dependency> <!-- Test dependencies --> From ca96cd82ac46b8e54fdd4271570d6c2e5941fd85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:58:09 +0100 Subject: [PATCH 0985/1008] chore: bump actions/checkout from 5.0.0 to 6.0.0 (#2308) Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/08c6903cd8c0fde910a37f88322edcfb5dd907a8...1af3b93b6815bc44a9784bd300feb67ff0d1eeb3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-build.yml | 4 ++-- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- .github/workflows/security-dependencies-check.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 8cef6040e..be53fbfaa 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -32,7 +32,7 @@ jobs: environment: Docs steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 with: fetch-depth: 0 - name: Build diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 88f1eabee..a972084a6 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -77,7 +77,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: @@ -94,7 +94,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 0 - name: Get changed files diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 54e7cd023..6ed3b248e 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -57,7 +57,7 @@ jobs: - 25 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: @@ -88,7 +88,7 @@ jobs: - 25 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index 41983f89b..66150821d 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -29,7 +29,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index bf746320d..8af0e4d9d 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -41,7 +41,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79cb5b04f..bd2e23487 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - id: version name: version uses: ./.github/actions/version @@ -224,7 +224,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: ref: ${{ env.RELEASE_COMMIT }} - id: download_source @@ -272,7 +272,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: # Checkout PR branch to make sure we build the version-bumped docs ref: ci-${{ github.run_id }} diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index c6e06950f..1e85d9e96 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -24,7 +24,7 @@ jobs: pull-requests: write steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Verify Contents uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 205cb0ddc..d59367015 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -35,7 +35,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Run Analysis From 54991e6bf589ac29b26960b53782ab5a8fe13371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:58:32 +0100 Subject: [PATCH 0986/1008] chore: bump sam/build-java25 (#2309) Bumps sam/build-java25 from `2ef9e5b` to `af599d0`. --- updated-dependencies: - dependency-name: sam/build-java25 dependency-version: af599d010afef63c83a04265dee9adbd4557d94b4a81941f28ae42657af7890d dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 5d374eaa8..6643760b4 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 25 -FROM public.ecr.aws/sam/build-java25@sha256:2ef9e5b950cc79489691be16c7edff904bf196955633dc7fbbc282a1ea421ba8 +FROM public.ecr.aws/sam/build-java25@sha256:af599d010afef63c83a04265dee9adbd4557d94b4a81941f28ae42657af7890d # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz From 93d302f09fbb37770bccedb96ab1e1acc5ea260e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:58:58 +0100 Subject: [PATCH 0987/1008] chore: bump github/codeql-action from 4.31.4 to 4.31.5 (#2312) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.4 to 4.31.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/e12f0178983d466f2f6028f5cc7a6d786fd97f4b...fdbfb4d2750291e159f0156def62b853c2798ca2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index d59367015..0944694f7 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v3.29.5 + uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.29.5 with: sarif_file: results.sarif From 7ea89ded9d25796e9acc0ae1a4b68b5ae4193481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:54:38 +0100 Subject: [PATCH 0988/1008] chore: bump aws-actions/configure-aws-credentials from 5.1.0 to 5.1.1 (#2316) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/00943011d9042930efac3dcd3a170e4273319bc8...61815dcd50bd041e203e49132bacad1fd04d2708) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-version: 5.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index be53fbfaa..417f21c48 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -41,7 +41,7 @@ jobs: 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@00943011d9042930efac3dcd3a170e4273319bc8 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 6ed3b248e..2ddb78ec0 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -65,7 +65,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 @@ -96,7 +96,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd2e23487..20ff19a69 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -282,7 +282,7 @@ jobs: 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@00943011d9042930efac3dcd3a170e4273319bc8 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} From bb70c1be87072674a46456cfb73cd86818f45d46 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Wed, 10 Dec 2025 16:21:53 +0100 Subject: [PATCH 0989/1008] fix(metrics): Clear custom dimensions when flushing. (#2328) --- .../metrics/internal/EmfMetricsLogger.java | 24 ++- .../internal/EmfMetricsLoggerTest.java | 189 ++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) 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 index 37f2d193a..611d4dcc6 100644 --- 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 @@ -164,6 +164,27 @@ public void flush() { } } 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); } } @@ -198,7 +219,8 @@ public void flushMetrics(Consumer<Metrics> metricsConsumer) { metrics.setNamespace(this.namespace); } if (!defaultDimensions.isEmpty()) { - metrics.setDefaultDimensions(software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions)); + metrics.setDefaultDimensions( + software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions)); } properties.forEach(metrics::addMetadata); 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 index c2238711a..5e597e835 100644 --- 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 @@ -263,6 +263,65 @@ void shouldAddMetadata() throws Exception { 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 @@ -547,4 +606,134 @@ void shouldNotFlushSingleMetricWhenDisabled() { 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(); + } } From 79e31370cf85d1c44b304930b4f063272a5e3a68 Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Mon, 15 Dec 2025 13:12:40 +0100 Subject: [PATCH 0990/1008] feat(cold-start-detection): Suppress cold start detection for non ON-DEMAND invocations (#2329) * fix: cold start only on on-demand invocation * fix: cold start only on on-demand invocation * fix: cold start only on on-demand invocation * mvn clean test is successful * reversed the sqllite changes * feat(cold-start-detection): Suppress cold start for non ON-DEMAND invocation (since there are no cold starts). --------- Co-authored-by: Attyuttam <attyuttam@gmail.com> --- .../terraform/pom.xml | 6 +++--- .../sam-graalvm/pom.xml | 7 +++---- .../powertools-examples-parameters/sam/pom.xml | 4 +--- pom.xml | 2 -- powertools-common/pom.xml | 1 - .../common/internal/LambdaConstants.java | 2 ++ .../common/internal/LambdaHandlerProcessor.java | 9 ++++++++- .../internal/LambdaHandlerProcessorTest.java | 1 + powertools-e2e-tests/pom.xml | 2 -- powertools-logging/pom.xml | 3 +-- .../powertools-logging-log4j/pom.xml | 3 +-- .../powertools-logging-logback/pom.xml | 3 +-- powertools-metrics/pom.xml | 1 + powertools-parameters/pom.xml | 2 -- .../powertools-parameters-appconfig/pom.xml | 2 -- .../powertools-parameters-dynamodb/pom.xml | 2 -- .../powertools-parameters-secrets/pom.xml | 2 -- .../powertools-parameters-ssm/pom.xml | 2 -- .../powertools-parameters-tests/pom.xml | 1 - powertools-serialization/pom.xml | 1 - powertools-tracing/pom.xml | 15 ++++++++++++++- 21 files changed, 36 insertions(+), 35 deletions(-) diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 14c0e223e..a6237b00f 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -1,5 +1,5 @@ <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> <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> @@ -101,7 +101,8 @@ <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> - <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> </transformers> </configuration> </execution> @@ -126,7 +127,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <environmentVariables> <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 6d0c3f4a2..09401eeff 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -1,5 +1,5 @@ <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>2.8.0</version> @@ -98,8 +98,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.5.4</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> @@ -144,7 +142,8 @@ <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> - <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/> + <transformer + implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" /> </transformers> </configuration> </execution> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 15d68ffcd..3e8f99aa0 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -1,5 +1,5 @@ <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>2.8.0</version> @@ -72,8 +72,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <!-- JUnit 5 requires Surefire version 3.1.0 or higher --> - <version>3.5.4</version> </plugin> <plugin> <groupId>dev.aspectj</groupId> diff --git a/pom.xml b/pom.xml index 1e4c3a9fd..6f6a9ce10 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,6 @@ <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.2.5</maven-surefire-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.12.0</maven-javadoc-plugin.version> @@ -625,7 +624,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> @{argLine} diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index f14783b5b..551fae8bc 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -106,7 +106,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java index 69fc1283a..4c4e8e9db 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java @@ -28,4 +28,6 @@ private LambdaConstants() { 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-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java index 393835d1e..15bff15d6 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java @@ -93,7 +93,14 @@ protected static void resetServiceName() { } public static boolean isColdStart() { - return isColdStart == 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() { diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index 5c6bdc020..0726a9e77 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -216,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(); diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 484e3b4c2..bfd4350a0 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -214,7 +214,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.5.4</version> <executions> <execution> <goals> @@ -241,7 +240,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>3.5.4</version> <executions> <execution> <goals> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index ae5788291..923d6d133 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -123,7 +123,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -182,11 +181,11 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <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> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 1cf3bf265..19a8b9428 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -112,7 +112,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -204,12 +203,12 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <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> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index b5a50573d..7ed1f1a17 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -110,7 +110,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -203,12 +202,12 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <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> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 4da0c0a9f..86bdcc560 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -231,6 +231,7 @@ <configuration> <environmentVariables> <AWS_EMF_ENVIRONMENT>Lambda</AWS_EMF_ENVIRONMENT> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> </environmentVariables> </configuration> </plugin> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index a2c4a65dc..78234b5cc 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -80,7 +80,6 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> @@ -98,7 +97,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index ab8ae0e67..3c38963a1 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -108,7 +108,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -175,7 +174,6 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 76a7b8845..b9e48567a 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -109,7 +109,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -176,7 +175,6 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index fbe4905be..c83c674d5 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -109,7 +109,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent @@ -176,7 +175,6 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index c9de1db51..d23b84bf6 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -98,7 +98,6 @@ <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <environmentVariables> <AWS_REGION>eu-central-1</AWS_REGION> @@ -123,7 +122,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 3bcd4d604..9b93b3f79 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -110,7 +110,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index c1eea4df7..535f23190 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -107,7 +107,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index ed2596059..9e5603d70 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -129,6 +129,20 @@ </dependency> </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <AWS_LAMBDA_INITIALIZATION_TYPE>on-demand</AWS_LAMBDA_INITIALIZATION_TYPE> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> + <profiles> <profile> <id>generate-graalvm-files</id> @@ -137,7 +151,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.5.4</version> <configuration> <argLine> -Dorg.graalvm.nativeimage.imagecode=agent From e6f6a92892291670387274d1d83253e577008722 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:09:57 +0100 Subject: [PATCH 0991/1008] chore: bump aws.sdk.version from 2.38.7 to 2.39.3 (#2313) Bumps `aws.sdk.version` from 2.38.7 to 2.39.3. Updates `software.amazon.awssdk:url-connection-client` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:sdk-core` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:s3` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:kinesis` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:sqs` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.38.7 to 2.39.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.39.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.39.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.39.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.39.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.39.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.39.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 6d941a31f..3a8e3ad8d 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ <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.38.7</sdk.version> + <sdk.version>2.39.3</sdk.version> </properties> <dependencies> From 314e56997a34273107ba4e13d5f1143a77d59600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:23 +0100 Subject: [PATCH 0992/1008] chore: bump com.google.protobuf:protobuf-java from 4.33.0 to 4.33.1 (#2314) Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 4.33.0 to 4.33.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-version: 4.33.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index f4b75722c..1b8e8f09d 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -12,7 +12,7 @@ <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.0</protobuf.version> + <protobuf.version>4.33.1</protobuf.version> </properties> <dependencies> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index ad564897f..82f6b8021 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -36,7 +36,7 @@ <properties> <kafka-clients.version>4.1.1</kafka-clients.version> <avro.version>1.12.1</avro.version> - <protobuf.version>4.33.0</protobuf.version> + <protobuf.version>4.33.1</protobuf.version> <lambda-serialization.version>1.1.6</lambda-serialization.version> </properties> From ff8122d90e4ef686613207fd91daf036e317babc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:33 +0100 Subject: [PATCH 0993/1008] chore: bump org.codehaus.mojo:versions-maven-plugin (#2315) Bumps [org.codehaus.mojo:versions-maven-plugin](https://github.com/mojohaus/versions) from 2.19.1 to 2.20.1. - [Release notes](https://github.com/mojohaus/versions/releases) - [Changelog](https://github.com/mojohaus/versions/blob/master/ReleaseNotes.md) - [Commits](https://github.com/mojohaus/versions/compare/2.19.1...2.20.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:versions-maven-plugin dependency-version: 2.20.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrea Amorosi <dreamorosi@gmail.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6f6a9ce10..f400e0b26 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ <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.19.1</versions-maven-plugin.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.20.0</mockito-junit-jupiter.version> From 057f94e2e2028291b88331c135c16093cc58fc86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:44 +0100 Subject: [PATCH 0994/1008] chore: bump graalvm/setup-graalvm from 1.4.2 to 1.4.4 (#2322) Bumps [graalvm/setup-graalvm](https://github.com/graalvm/setup-graalvm) from 1.4.2 to 1.4.4. - [Release notes](https://github.com/graalvm/setup-graalvm/releases) - [Commits](https://github.com/graalvm/setup-graalvm/compare/eec48106e0bf45f2976c2ff0c3e22395cced8243...790e28947b79a9c09c3391c0f18bf8d0f102ed69) --- updated-dependencies: - dependency-name: graalvm/setup-graalvm dependency-version: 1.4.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index a972084a6..f23968815 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -105,7 +105,7 @@ jobs: powertools-*/** pom.xml - name: Setup GraalVM - uses: graalvm/setup-graalvm@eec48106e0bf45f2976c2ff0c3e22395cced8243 # v1.4.2 + uses: graalvm/setup-graalvm@790e28947b79a9c09c3391c0f18bf8d0f102ed69 # v1.4.4 with: java-version: "21" distribution: "graalvm" From 9f4668a7d75285a529893abd28415b578a9aab5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:56 +0100 Subject: [PATCH 0995/1008] chore: bump actions/checkout from 6.0.0 to 6.0.1 (#2323) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/1af3b93b6815bc44a9784bd300feb67ff0d1eeb3...8e8c483db84b4bee98b60c0593521ed34d9990e8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-build.yml | 4 ++-- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- .github/workflows/security-dependencies-check.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 417f21c48..a94ace711 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -32,7 +32,7 @@ jobs: environment: Docs steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: fetch-depth: 0 - name: Build diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index f23968815..108a32a11 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -77,7 +77,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: @@ -94,7 +94,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 - name: Get changed files diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 2ddb78ec0..8a527835d 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -57,7 +57,7 @@ jobs: - 25 steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: @@ -88,7 +88,7 @@ jobs: - 25 steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index 66150821d..cab6c16a8 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -29,7 +29,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index 8af0e4d9d..db4014aea 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -41,7 +41,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20ff19a69..a59cab7cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - id: version name: version uses: ./.github/actions/version @@ -224,7 +224,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ env.RELEASE_COMMIT }} - id: download_source @@ -272,7 +272,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + 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 }} diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 1e85d9e96..6729fd304 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -24,7 +24,7 @@ jobs: pull-requests: write steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Verify Contents uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 0944694f7..e15b0b5dc 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -35,7 +35,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Run Analysis From 8fd60742bd06c695c9ab55f63701a38630a3d9fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:08 +0100 Subject: [PATCH 0996/1008] chore: bump sam/build-java25 (#2324) Bumps sam/build-java25 from `af599d0` to `b34fc78`. --- updated-dependencies: - dependency-name: sam/build-java25 dependency-version: b34fc789a70464f080232ee96590a6d0c7249eb59b1c33deccaae08a882cac23 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 6643760b4..fbefe46dd 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 25 -FROM public.ecr.aws/sam/build-java25@sha256:af599d010afef63c83a04265dee9adbd4557d94b4a81941f28ae42657af7890d +FROM public.ecr.aws/sam/build-java25@sha256:b34fc789a70464f080232ee96590a6d0c7249eb59b1c33deccaae08a882cac23 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz From 8d5a364e795cfa28c9911fd89d8d8283c029a308 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:21 +0100 Subject: [PATCH 0997/1008] chore: bump actions/setup-java from 5.0.0 to 5.1.0 (#2325) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/dded0888837ed1f317902acf8a20df0ad188d165...f2beeb24e141e01a676f977032f5a29d81c9e27e) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 5.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 108a32a11..cdfd5aea5 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -79,7 +79,7 @@ jobs: name: Checkout repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 8a527835d..378d48a60 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -59,7 +59,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index cab6c16a8..7e7dce429 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: java-version: 21 distribution: corretto diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index db4014aea..c5c8197f9 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -43,7 +43,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: distribution: 'corretto' java-version: 21 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a59cab7cb..c9b2d80c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,7 +137,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: 21 @@ -172,7 +172,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: ${{ matrix.java }} @@ -195,7 +195,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: 21 From a94274eba5ca1bf04432910db7776e551f236799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:32 +0100 Subject: [PATCH 0998/1008] chore: bump tj-actions/changed-files from 47.0.0 to 47.0.1 (#2330) Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 47.0.0 to 47.0.1. - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/24d32ffd492484c1d75e0c0b894501ddb9d30d62...e0021407031f5be11a464abee9a0776171c79891) --- updated-dependencies: - dependency-name: tj-actions/changed-files dependency-version: 47.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index cdfd5aea5..339d6fab8 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -99,7 +99,7 @@ jobs: fetch-depth: 0 - name: Get changed files id: changed-files - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0 + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 with: files: | powertools-*/** From ff3e3e6a3470199773715e51626f60d99338598a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:42 +0100 Subject: [PATCH 0999/1008] chore: bump github/codeql-action from 4.31.5 to 4.31.8 (#2331) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.5 to 4.31.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/fdbfb4d2750291e159f0156def62b853c2798ca2...1b168cd39490f61582a9beae412bb7057a6b2c4e) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index e15b0b5dc..7f3286dfe 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.29.5 + uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 with: sarif_file: results.sarif From 42567838f53a764516f12126fe76d3bb289fb09a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 11:34:12 +0100 Subject: [PATCH 1000/1008] chore(ci): bump version to 2.9.0 (#2332) * chore(ci): bump version to 2.9.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page <pagejep@amazon.com> --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- .../main/java/org/demo/kafka/protobuf/ProtobufProduct.java | 4 ++-- .../org/demo/kafka/protobuf/ProtobufProductOrBuilder.java | 2 +- .../org/demo/kafka/protobuf/ProtobufProductOuterClass.java | 4 ++-- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 65 files changed, 74 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 2644c47ab..4c02e2d1f 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging-log4j</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </dependency> ... </dependencies> diff --git a/examples/pom.xml b/examples/pom.xml index 511cdd5a9..5d191063f 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-examples</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Examples</name> diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 3a8e3ad8d..0091fb5ca 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-batch</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Batch</name> diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index b9e58eb82..27e564bf3 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.9.0718 ``` ### Deploy with CDK @@ -32,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-2.8.0718 +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/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index c2a9e2ffd..3aca1408a 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 +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) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 09adbfa18..8581a6ceb 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-cloudformation</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 29acef5e2..28be679a5 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ <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.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-core-utilities-cdk</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 7daf3153f..e3ceb7e65 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> <artifactId>cdk</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.224.0</cdk.version> diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 6841d20ab..b01fdcfaa 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { 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.8.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.8.0' - aspect 'software.amazon.lambda:powertools-metrics:2.8.0' + 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/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index bc11eb32e..3dae5015e 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { 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.8.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.8.0") - aspect("software.amazon.lambda:powertools-metrics:2.8.0") + 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") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index fceb4a322..e7a3f202d 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-core-utilities-sam-graalvm</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index b8606a227..2d6a00161 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-core-utilities-sam</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index d77b0ccd6..26e647dad 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-core-utilities-serverless</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index a6237b00f..4de1e415c 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform</name> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-core-utilities-terraform</artifactId> <packaging>jar</packaging> diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index d1c0205ff..0536951aa 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.8.0</version> + <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> diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index b90705c67..22d6a9c81 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>software.amazon.lambda.examples</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-idempotency</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Idempotency</name> diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 1b8e8f09d..d152f46c0 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ 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.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-kafka</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Kafka</name> 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 index 13d8905f2..2bf5db844 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.0 +// Protobuf Java Version: 4.33.1 package org.demo.kafka.protobuf; @@ -19,7 +19,7 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 33, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", "ProtobufProduct"); } 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 index ae7cb2182..caf17ad50 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.0 +// Protobuf Java Version: 4.33.1 package org.demo.kafka.protobuf; 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 index 4fcac7bd7..ce3214777 100644 --- 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 @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.0 +// Protobuf Java Version: 4.33.1 package org.demo.kafka.protobuf; @@ -13,7 +13,7 @@ private ProtobufProductOuterClass() {} com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 33, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", "ProtobufProductOuterClass"); } diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 09401eeff..7fc652bca 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.8.0</version> + <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> diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 3e8f99aa0..d2c3e68d2 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ 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.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-parameters-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Parameters</name> diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 44661fb71..5077c8989 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ 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.8.0</version> + <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> diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 93c60a2cb..cf66c3e14 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ 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.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-serialization-sam</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Serialization</name> diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1337bc2ee..2fa8462a5 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ 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.8.0</version> + <version>2.9.0</version> <artifactId>powertools-examples-validation</artifactId> <packaging>jar</packaging> <name>Powertools for AWS Lambda (Java) - Examples - Validation</name> diff --git a/mkdocs.yml b/mkdocs.yml index 5f35b96c0..b52b88cca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -129,7 +129,7 @@ extra_javascript: extra: powertools: - version: 2.8.0 + version: 2.9.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index f400e0b26..b300e911d 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <packaging>pom</packaging> <name>Powertools for AWS Lambda (Java) - Parent</name> diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 07d11d41c..37cfdf7b2 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <description>A suite of utilities that makes batch message processing using AWS Lambda easier.</description> diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 09e3299fd..cb06dc1f3 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Cloudformation</name> diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 551fae8bc..75ef10beb 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Common Internal Utilities</name> diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index f8e0a037f..3e89aadd2 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-batch</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index 2b53dc275..b5669b21f 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-idempotency-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index 76e65edb1..21a658e6c 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-idempotency-generics</artifactId> diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 36181ea01..921599bdb 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-idempotency</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index d29017074..ddfe39a5e 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-largemessage-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 53b1e7c10..bee253988 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-largemessage</artifactId> diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 9f6f23389..5ef7e1963 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-large-msg-idempotent</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index 4616af233..4ec6e5008 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-logging-functional</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index bba711163..022f029e6 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-logging-log4j</artifactId> diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index bd442fe88..f8458db25 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-logging-logback</artifactId> diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 90704c6d9..ddc6ae1bd 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-metrics</artifactId> diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 670eeb9c0..fb2deb2aa 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-parameters</artifactId> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index a9d3c3ab5..54f07d681 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.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> diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 89891df8c..9874ce986 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-tracing</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index 8e5e4dadb..14dbb9b13 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-validation-alb-event</artifactId> diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 97e967c3b..290e47b13 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>e2e-test-handlers-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>e2e-test-handler-validation-apigw-event</artifactId> diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index bfd4350a0..fec4dec92 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-e2e-tests</artifactId> diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index d15d3445d..cbe2384ba 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-idempotency</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 801664b4a..4cba1956f 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-idempotency-core</artifactId> diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index a9cf5739b..d223e0d2f 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-idempotency</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-idempotency-dynamodb</artifactId> diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 82f6b8021..c71ef94f6 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-kafka</artifactId> diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index d72877552..ad5910ec1 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-large-messages</artifactId> diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 923d6d133..200358e0b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Logging</name> diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 19a8b9428..aa4aca181 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 7ed1f1a17..dbf7f5207 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 86bdcc560..5f6c971e3 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Metrics</name> diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 78234b5cc..124e22186 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-parameters</artifactId> diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 3c38963a1..406f715d3 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index b9e48567a..eb5604046 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index c83c674d5..b9535269e 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index d23b84bf6..e0253e10b 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 9b93b3f79..fa2542730 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ <parent> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-parent</artifactId> - <version>2.8.0</version> + <version>2.9.0</version> <relativePath>../../pom.xml</relativePath> </parent> diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 535f23190..81603cd4f 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <artifactId>powertools-serialization</artifactId> diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 9e5603d70..545633c50 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Tracing</name> diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 37063cf6d..8dfce6b6a 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ <parent> <artifactId>powertools-parent</artifactId> <groupId>software.amazon.lambda</groupId> - <version>2.8.0</version> + <version>2.9.0</version> </parent> <name>Powertools for AWS Lambda (Java) - Validation</name> From 058c61f57f172715328b2c76dc5a32b4fd765a1c Mon Sep 17 00:00:00 2001 From: Philipp Page <github@philipp.page> Date: Tue, 16 Dec 2025 11:46:06 +0100 Subject: [PATCH 1001/1008] docs: Announce end-of-life of version 1.x.x (#2333) --- docs/processes/versioning.md | 8 ++++---- docs/upgrade.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/processes/versioning.md b/docs/processes/versioning.md index bbb60f507..d20269001 100644 --- a/docs/processes/versioning.md +++ b/docs/processes/versioning.md @@ -55,7 +55,7 @@ To see the list of available major versions of Powertools for AWS Lambda and whe ### 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 | Maintenance | 11/04/2020 | End-of-support: December 12, 2025. See [upgrade guide](https://docs.powertools.aws.dev/lambda/java/latest/upgrade/) | +| 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/upgrade.md b/docs/upgrade.md index 5b6d16d99..c9662a3db 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -7,7 +7,7 @@ description: Guide to update between major Powertools for AWS Lambda (Java) vers <!-- prettier-ignore-start --> !!! warning "End of support notice" - On December 12th, 2025, Powertools for AWS Lambda (Java) v1 will reach end of support 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. + 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. From df51f8b56ce63af4e2fc86a31ba90daaf2a9d38b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:23:43 +0000 Subject: [PATCH 1002/1008] chore: bump aws.sdk.version from 2.39.1 to 2.40.9 (#2335) Bumps `aws.sdk.version` from 2.39.1 to 2.40.9. Updates `software.amazon.awssdk:bom` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:http-client-spi` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:url-connection-client` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:s3` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:dynamodb` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:lambda` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:kinesis` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:cloudwatch` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:xray` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:sqs` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:cloudformation` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:sts` from 2.39.1 to 2.40.9 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.40.9 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.40.9 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.40.9 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8581a6ceb..212c0966b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ <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.39.1</aws.sdk.version> + <aws.sdk.version>2.40.9</aws.sdk.version> <aspectj.version>1.9.20.1</aspectj.version> </properties> diff --git a/pom.xml b/pom.xml index b300e911d..a7b3d2fee 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ <log4j.version>2.25.2</log4j.version> <slf4j.version>2.0.17</slf4j.version> <jackson.version>2.20.1</jackson.version> - <aws.sdk.version>2.39.1</aws.sdk.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> diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 54f07d681..477b49dc0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ <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.39.1</aws.sdk.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> From e8b1bb7cd74ab69a2c98c1afc0299ad6e683ae5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:24:37 +0000 Subject: [PATCH 1003/1008] chore: bump org.mockito:mockito-junit-jupiter from 5.20.0 to 5.21.0 (#2336) Bumps [org.mockito:mockito-junit-jupiter](https://github.com/mockito/mockito) from 5.20.0 to 5.21.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.20.0...v5.21.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-junit-jupiter dependency-version: 5.21.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7b3d2fee..6188b47a4 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ <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.20.0</mockito-junit-jupiter.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> From 9a18c5d5356053993e60bfdc3a4a94212b9d68d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:24:48 +0000 Subject: [PATCH 1004/1008] chore: bump actions/upload-artifact from 5.0.0 to 6.0.0 (#2338) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/330a01c490aca151604b8cf639adc76d48f6c5d4...b7c566a772e6b6bfb58ed0dc250532a479d7789f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c9b2d80c6..9e52301dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,7 +112,7 @@ jobs: snapshot: ${{ inputs.snapshot}} - id: upload_source name: Upload artifacts - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: if-no-files-found: error name: source diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 7f3286dfe..f9aaaea52 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -46,7 +46,7 @@ jobs: publish_results: true repo_token: ${{ secrets.SCORECARD_TOKEN }} - name: Upload Results - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif From 9ee9a95081f1559fa86f04221cbf6aa5b8c692dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:24:59 +0000 Subject: [PATCH 1005/1008] chore: bump actions/download-artifact from 6.0.0 to 7.0.0 (#2339) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/018cc2cf5baa6db3ef3c5f8a56943fffe632ef53...37930b1c2abaa49bbe596cd826c3c89aef350131) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e52301dd..630b91321 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - name: Setup Java @@ -168,7 +168,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - name: Setup Java @@ -191,7 +191,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - name: Setup Java @@ -229,7 +229,7 @@ jobs: ref: ${{ env.RELEASE_COMMIT }} - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - id: setup-git From ad640f7a61385f20208c8dc3a0296e162934e138 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:25:08 +0000 Subject: [PATCH 1006/1008] chore: bump github/codeql-action from 4.31.8 to 4.31.9 (#2340) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.8 to 4.31.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/1b168cd39490f61582a9beae412bb7057a6b2c4e...5d4e8d1aca955e8d8589aabd499c5cae939e33c7) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index f9aaaea52..b91e78c69 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5 with: sarif_file: results.sarif From 21e1fcffc3793c51235bcfd6778c483d57320dae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:25:44 +0000 Subject: [PATCH 1007/1008] chore: bump sam/build-java25 (#2342) Bumps sam/build-java25 from `b34fc78` to `bffac7d`. --- updated-dependencies: - dependency-name: sam/build-java25 dependency-version: bffac7de6e418a93d2aefc1e8e7c79eda0971e7a026725fe618b58ddfba7a128 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index fbefe46dd..1ceb29aa0 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 25 -FROM public.ecr.aws/sam/build-java25@sha256:b34fc789a70464f080232ee96590a6d0c7249eb59b1c33deccaae08a882cac23 +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 From 54fe0be69938dbecb0d4efaa6c0152ee20c34eac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:25:57 +0000 Subject: [PATCH 1008/1008] chore: bump org.apache.logging.log4j:log4j-core from 2.25.2 to 2.25.3 (#2344) Bumps org.apache.logging.log4j:log4j-core from 2.25.2 to 2.25.3. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 28be679a5..c02b73026 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -11,7 +11,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.25.2</log4j.version> + <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> diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index e7a3f202d..eea0357e9 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -9,7 +9,7 @@ <packaging>jar</packaging> <properties> - <log4j.version>2.25.2</log4j.version> + <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> diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 7fc652bca..2aaffd9d1 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -8,7 +8,7 @@ <name>Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM</name> <properties> - <log4j.version>2.25.2</log4j.version> + <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> diff --git a/pom.xml b/pom.xml index 6188b47a4..e6cf78b14 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ <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.2</log4j.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>