diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml index 6d737ee55..8390cfb5b 100644 --- a/.github/dependency-review-config.yml +++ b/.github/dependency-review-config.yml @@ -27,4 +27,5 @@ 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 + - '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..02920d6e7 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 @@ -96,13 +76,39 @@ jobs: name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 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 + mvn -B 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: | + # Find modules with graalvm-native profile and run tests. + # This will make sure to discover new GraalVM supported modules automatically in the future. + for module in powertools-*/pom.xml; do + if grep -q "graalvm-native" "$module"; then + module_dir=$(dirname "$module") + echo "Regenerating GraalVM metadata for $module_dir" + mvn -B -f "$module" -Pgenerate-graalvm-files clean test + echo "Running GraalVM native tests for $module_dir" + mvn -B -f "$module" -Pgraalvm-native test + fi + done 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 @@ powertools-examples-core-utilities/cdk/infra powertools-examples-core-utilities/serverless powertools-examples-core-utilities/terraform - powertools-examples-idempotency + powertools-examples-idempotency/sam + powertools-examples-idempotency/sam-graalvm powertools-examples-parameters/sam powertools-examples-parameters/sam-graalvm powertools-examples-serialization/sam diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 3df44f441..b184f402f 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -45,6 +45,11 @@ aspectjrt ${aspectj.version} + + software.amazon.awssdk + s3 + 2.32.8 + 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..4a1d86205 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,13 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.metrics.FlushMetrics; import software.amazon.lambda.powertools.metrics.Metrics; @@ -50,13 +57,32 @@ */ public class App implements RequestHandler { private static final Logger log = LoggerFactory.getLogger(App.class); + private static final S3Client s3Client = S3Client.builder() + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .overrideConfiguration(c -> c.addExecutionInterceptor(new ExecutionInterceptor() { + @Override + public void beforeTransmission( + software.amazon.awssdk.core.interceptor.Context.BeforeTransmission context, + ExecutionAttributes executionAttributes) { + SdkHttpRequest request = context.httpRequest(); + log.info("User-Agent header: {}", request.headers().get("User-Agent")); + } + })) + .build(); private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + public App() { + log.info("sdk.ua.appId system property: {}", System.getProperty("sdk.ua.appId")); + s3Client.listBuckets(); + } + @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 headers = new HashMap<>(); + s3Client.listBuckets(); headers.put("Content-Type", "application/json"); headers.put("X-Custom-Header", "application/json"); diff --git a/examples/powertools-examples-core-utilities/sam/template.yaml b/examples/powertools-examples-core-utilities/sam/template.yaml index 9a51a1ba9..af0a553fb 100644 --- a/examples/powertools-examples-core-utilities/sam/template.yaml +++ b/examples/powertools-examples-core-utilities/sam/template.yaml @@ -19,11 +19,22 @@ Globals: POWERTOOLS_METRICS_NAMESPACE: Coreutilities Resources: + TestBucket: + Type: AWS::S3::Bucket + 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 + Policies: + - S3ReadPolicy: + BucketName: !Ref TestBucket + - Statement: + - Effect: Allow + Action: + - s3:ListAllMyBuckets + Resource: "*" 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 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= +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 @@ + + 4.0.0 + software.amazon.lambda.examples + 2.3.0 + powertools-examples-idempotency-sam-graalvm + jar + Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM + + + 11 + 11 + 1.9.20.1 + + + + + software.amazon.lambda + powertools-tracing + ${project.version} + + + software.amazon.lambda + powertools-logging-log4j + ${project.version} + + + software.amazon.lambda + powertools-idempotency-dynamodb + ${project.version} + + + + com.amazonaws + aws-lambda-java-core + 1.3.0 + + + com.amazonaws + aws-lambda-java-events + 3.16.1 + + + com.amazonaws + aws-lambda-java-runtime-interface-client + 2.8.3 + + + + org.aspectj + aspectjrt + ${aspectj.version} + + + + + + + dev.aspectj + aspectj-maven-plugin + 1.14.1 + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.target} + + + software.amazon.lambda + powertools-tracing + + + software.amazon.lambda + powertools-logging + + + software.amazon.lambda + powertools-idempotency-core + + + + + + + compile + + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + false + + + + + + + + + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.2.0 + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.4 + + true + + + + + + + native-image + + + + org.graalvm.buildtools + native-maven-plugin + 0.11.0 + true + + + build-native + + build + + package + + + + hello-world + com.amazonaws.services.lambda.runtime.api.client.AWSLambda + + + --enable-url-protocols=http,https + --add-opens java.base/java.util=ALL-UNNAMED + + + + + + + + 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":"","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"","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..ddda5d5f1 --- /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,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-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":"","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":"","parameterTypes":[] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields":[{"name":"logger"}] + }, + { + "name":"java.lang.Void", + "methods":[{"name":"","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": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "", "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 65% rename from examples/powertools-examples-idempotency/pom.xml rename to examples/powertools-examples-idempotency/sam/pom.xml index eb235437b..1b82ab3ed 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -13,7 +13,7 @@ --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples @@ -44,6 +44,7 @@ powertools-idempotency-dynamodb ${project.version} + com.amazonaws aws-lambda-java-core @@ -54,24 +55,12 @@ aws-lambda-java-events 3.16.1 + org.aspectj aspectjrt ${aspectj.version} - - - org.mockito - mockito-core - 5.18.0 - test - - - org.junit.jupiter - junit-jupiter-api - 5.11.1 - test - @@ -114,39 +103,7 @@ - - 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-surefire-plugin - 3.5.3 - - - ${project.build.directory}/native-libs - - - idempotency - eu-central-1 - LOG_ERROR - - - + org.apache.maven.plugins maven-shade-plugin @@ -159,19 +116,9 @@ false - - - - - - org.apache.logging.log4j - log4j-transform-maven-shade-plugin-extensions - 0.2.0 - - org.apache.maven.plugins 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 { + 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 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. + *

+ * You can test the endpoint like this: + * + *

+     *     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"}'
+     * 
+ *
    + *
  • 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).
  • + *
+ */ + @Idempotent // The magic is here! + @Logging(logEvent = true) + @Tracing + 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 { + // 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. + *

+ * 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 @@ + + + + + + + + + + + + + + + + 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 5896ffeb7..bdafe88e1 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,6 @@ 3.11.3 3.3.1 3.2.1 - 5.10.0 1.14.1 3.5.3 0.8.13 @@ -115,8 +114,8 @@ 1.12.781 2.18.0 1.7.0 - 5.18.0 - 5.18.0 + 5.19.1-SNAPSHOT + 5.19.1-SNAPSHOT 2.3.0 1.5.0 @@ -132,6 +131,21 @@ + + + + Central Portal Snapshots + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + + + + @@ -323,7 +337,7 @@ org.junit-pioneer junit-pioneer - 2.0.0 + ${junit-pioneer.version} test @@ -445,6 +459,11 @@ maven-gpg-plugin ${maven-gpg-plugin.version} + + org.codehaus.mojo + exec-maven-plugin + 3.5.1 + diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 9f8456842..f4c4a6a7c 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -14,8 +14,8 @@ --> + 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"> 4.0.0 powertools-common @@ -76,11 +76,6 @@ assertj-core test - - org.mockito - mockito-core - test - org.slf4j slf4j-simple @@ -90,14 +85,6 @@ generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -105,7 +92,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -116,20 +105,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -147,32 +128,16 @@ --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun --initialize-at-build-time=org.slf4j.simple.SimpleLogger --initialize-at-build-time=org.slf4j.LoggerFactory --initialize-at-build-time=org.junit.Ignore --initialize-at-build-time=java.lang.annotation.Annotation --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces @@ -192,5 +157,18 @@ src/main/resources + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + 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..264598788 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 @@ -19,14 +19,14 @@ import java.io.IOException; import java.io.InputStream; 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"; @@ -37,7 +37,7 @@ public class UserAgentConfigurator { 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 +53,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. @@ -83,9 +82,18 @@ static String getVersionFromProperties(String propertyFileName, String versionKe return NA; } + /** + * Configures the AWS SDK to use Powertools user agent by setting the sdk.ua.appId system property. + * This should be called during library initialization to ensure the user agent is properly configured. + */ + public static void configureUserAgent(String ptFeature) { + System.out.println("CONFIGURE UA " + ptFeature); + System.setProperty("sdk.ua.appId", getUserAgent(ptFeature)); + } + /** * 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" @@ -105,7 +113,7 @@ public static String getUserAgent(String ptFeature) { ptFeature = NO_OP; } return userAgent - .replace(PT_FEATURE_VARIABLE, ptFeature) + .replace(PT_FEATURE_VARIABLE, ptFeature.toUpperCase()) .replace(PT_EXEC_ENV_VARIABLE, ptExecEnv); } } 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..11eac7a4c 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 @@ -16,15 +16,10 @@ 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.when; -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.junitpioneer.jupiter.ClearEnvironmentVariable; import org.junitpioneer.jupiter.SetEnvironmentVariable; @@ -33,14 +28,18 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -class LambdaHandlerProcessorTest { +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.common.stubs.TestInputStream; +import software.amazon.lambda.powertools.common.stubs.TestOutputStream; +import software.amazon.lambda.powertools.common.stubs.TestProceedingJoinPoint; +import software.amazon.lambda.powertools.common.stubs.TestSignature; - 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 +47,7 @@ void isHandlerMethod_shouldRecognizeRequestHandler() { @Test void isHandlerMethod_shouldRecognizeRequestStreamHandler() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + Object[] args = { new TestInputStream(), new TestOutputStream(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); assertThat(LambdaHandlerProcessor.isHandlerMethod(pjpMock)).isTrue(); @@ -65,7 +64,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 +72,7 @@ void placedOnRequestHandler_shouldRecognizeRequestHandler() { @Test void placedOnStreamHandler_shouldRecognizeRequestStreamHandler() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + Object[] args = { new TestInputStream(), new TestOutputStream(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); assertThat(LambdaHandlerProcessor.placedOnStreamHandler(pjpMock)).isTrue(); @@ -81,7 +80,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 +90,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 +100,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 +110,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 +120,7 @@ void placedOnStreamHandler_shouldInvalidateOnWrongTypeOfArgs() { @Test void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidOutputStreamArg() { - Object[] args = {mock(InputStream.class), new Object(), mock(Context.class)}; + Object[] args = { new TestInputStream(), new Object(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); @@ -131,7 +130,7 @@ void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidOutputStreamArg() @Test void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidContextArg() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), new Object()}; + Object[] args = { new TestInputStream(), new TestOutputStream(), new Object() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); boolean isPlacedOnStreamHandler = LambdaHandlerProcessor.placedOnStreamHandler(pjpMock); @@ -144,9 +143,9 @@ void placedOnStreamHandler_shouldInvalidateOnTypeOfArgs_invalidContextArg() { void getXrayTraceId_present() { String traceID = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""; - Optional xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); + Optional xRayTraceId = LambdaHandlerProcessor.getXrayTraceId(); - assertThat(xRayTraceId.isPresent()).isTrue(); + assertThat(xRayTraceId).isPresent(); assertThat(traceID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, "")).isEqualTo(xRayTraceId.get()); } @@ -161,7 +160,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 +170,7 @@ void extractContext_fromRequestHandler() { @Test void extractContext_fromStreamRequestHandler() { - Object[] args = {mock(InputStream.class), mock(OutputStream.class), mock(Context.class)}; + Object[] args = { new TestInputStream(), new TestOutputStream(), new TestLambdaContext() }; ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(RequestStreamHandler.class, args); Context context = LambdaHandlerProcessor.extractContext(pjpMock); @@ -181,7 +180,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 +229,8 @@ void serviceName_Undefined() { 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; + private ProceedingJoinPoint mockRequestHandlerPjp(Class handlerClass, Object[] handlerArgs) { + TestSignature signature = new TestSignature(handlerClass); + return new TestProceedingJoinPoint(signature, handlerArgs); } } 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..ba33de49a 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 @@ -91,7 +91,7 @@ void testGetUserAgent() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/test-feature/" + VERSION + " PTEnv/NA"); + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); } @@ -101,7 +101,7 @@ void testGetUserAgent_NoFeature() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA"); + .isEqualTo("PT/NO-OP/" + VERSION + " PTENV/NA"); } @Test @@ -110,7 +110,7 @@ void testGetUserAgent_NullFeature() { assertThat(userAgent) .isNotNull() - .isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA"); + .isEqualTo("PT/NO-OP/" + VERSION + " PTENV/NA"); } @Test @@ -120,7 +120,15 @@ 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() { + UserAgentConfigurator.configureUserAgent("test-feature"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); } } diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestInputStream.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestInputStream.java new file mode 100644 index 000000000..cb9122966 --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestInputStream.java @@ -0,0 +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.common.stubs; + +import java.io.InputStream; + +public class TestInputStream extends InputStream { + @Override + public int read() { + return -1; + } +} 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-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestOutputStream.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestOutputStream.java new file mode 100644 index 000000000..b64b4759d --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestOutputStream.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.common.stubs; + +import java.io.OutputStream; + +public class TestOutputStream extends OutputStream { + @Override + public void write(int b) { + } +} diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestProceedingJoinPoint.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestProceedingJoinPoint.java new file mode 100644 index 000000000..e0285518c --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestProceedingJoinPoint.java @@ -0,0 +1,90 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.common.stubs; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.reflect.SourceLocation; +import org.aspectj.runtime.internal.AroundClosure; + +public class TestProceedingJoinPoint implements ProceedingJoinPoint { + private final Signature signature; + private final Object[] args; + + public TestProceedingJoinPoint(Signature signature, Object[] args) { + this.signature = signature; + this.args = args; + } + + @Override + public Object[] getArgs() { + return args; + } + + @Override + public Signature getSignature() { + return signature; + } + + @Override + public Object getTarget() { + return null; + } + + @Override + public Object getThis() { + return null; + } + + @Override + public StaticPart getStaticPart() { + return null; + } + + @Override + public String getKind() { + return null; + } + + @Override + public SourceLocation getSourceLocation() { + return null; + } + + @Override + public Object proceed() { + return null; + } + + @Override + public Object proceed(Object[] args) { + return null; + } + + @Override + public void set$AroundClosure(AroundClosure arc) { + // Stubbed method + } + + @Override + public String toLongString() { + return "handleRequest"; + } + + @Override + public String toShortString() { + return "handleRequest"; + } +} diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestSignature.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestSignature.java new file mode 100644 index 000000000..903e4c8fa --- /dev/null +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestSignature.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.common.stubs; + +import org.aspectj.lang.Signature; + +public class TestSignature implements Signature { + private final Class declaringType; + + public TestSignature(Class declaringType) { + this.declaringType = declaringType; + } + + @Override + public Class getDeclaringType() { + return declaringType; + } + + @Override + public String getDeclaringTypeName() { + return declaringType.getName(); + } + + @Override + public int getModifiers() { + return 0; + } + + @Override + public String getName() { + return "handleRequest"; + } + + @Override + public String toLongString() { + return "handleRequest"; + } + + @Override + public String toShortString() { + return "handleRequest"; + } +} diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 6f15bfc3d..ba04019aa 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -37,16 +37,110 @@ software.amazon.lambda powertools-serialization + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + org.mockito mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple test + + software.amazon.lambda + powertools-common + ${project.version} + test-jar + test + - \ No newline at end of file + + + + generate-graalvm-files + + + org.mockito + mockito-subclass + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + -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 + + + + + + + + graalvm-native + + + org.mockito + mockito-subclass + test + + + + + + org.graalvm.buildtools + native-maven-plugin + 0.11.0 + true + + + test-native + + test + + test + + + + powertools-idempotency-core + + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + --no-fallback + --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces + + + + + + + + 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..398beee2c 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,20 +24,20 @@ 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 software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -61,19 +61,14 @@ import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; +@ExtendWith(MockitoExtension.class) public 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() { Idempotency.config() @@ -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); @@ -124,8 +117,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 @@ -393,8 +384,6 @@ public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { // 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); 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..78b3d6ad4 --- /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 \ 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..ef5a3d6eb 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -63,6 +63,29 @@ + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.slf4j + slf4j-simple + test + + + software.amazon.lambda + powertools-common + ${project.version} + test-jar + test + + com.amazonaws DynamoDBLocal @@ -71,17 +94,135 @@ 2.2.0 test - - - io.github.ganadist.sqlite4java - libsqlite4java-osx-aarch64 - 1.0.392 - test - dylib - + + + + generate-graalvm-files + + + + com.amazonaws + aws-xray-recorder-sdk-aws-sdk-v2-instrumentor + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + -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 + + + + + + + + graalvm-native + + + + org.graalvm.buildtools + native-maven-plugin + 0.11.0 + true + + + test-native + + test + + test + + + + powertools-idempotency-dynamodb + + + com.amazonaws + DynamoDBLocal + + + + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + --enable-url-protocols=http + --no-fallback + --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces + + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dynamodb-local + generate-test-resources + + copy-dependencies + + + test + false + + com.amazonaws,software.amazon.awssdk,org.eclipse.jetty,com.fasterxml.jackson.core,com.fasterxml.jackson.dataformat,com.fasterxml.jackson.datatype,com.almworks.sqlite4java,commons-cli,org.apache.commons,commons-logging,org.apache.logging.log4j,org.slf4j,org.reactivestreams,org.antlr,com.google.guava,jakarta.transaction + ${project.build.directory}/dynamodb-local + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + start-dynamodb-local + process-test-classes + + exec + + + java + ${project.build.directory}/dynamodb-local + + -Djava.library.path=${project.build.directory}/dynamodb-local + -cp + ${project.build.directory}/dynamodb-local/* + com.amazonaws.services.dynamodbv2.local.main.ServerRunner + -inMemory + -port + 8000 + + true + true + + + + org.apache.maven.plugins maven-jar-plugin @@ -94,6 +235,16 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + + + http://localhost:8000 + + + dev.aspectj aspectj-maven-plugin 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":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.services.lambda.runtime.Context" +}, +{ + "name":"com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","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":"","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":"","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":"","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":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandlerManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandlerRequestDescriptor", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSOperationHandlerResponseDescriptor", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.handlers.config.AWSServiceHandlerManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.interceptors.TracingInterceptor", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.amazonaws.xray.strategy.sampling.manifest.SamplingRuleManifest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","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":"","parameterTypes":[] }, {"name":"setFixedTarget","parameterTypes":["int"] }, {"name":"setRate","parameterTypes":["float"] }] +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ARCFOURCipher", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESCipher", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DESedeCipher", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.HmacCore$HmacSHA256", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"java.io.IOException", + "methods":[{"name":"","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":"","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":"","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":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.commons.logging.impl.WeakHashtable", + "methods":[{"name":"","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":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.MD5", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA2$SHA256", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.NetscapeCertTypeExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.PrivateKeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"","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 key; private DynamoDBPersistenceStore dynamoDBPersistenceStore; - // ================================================================= - // @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 item = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + 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_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 itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + 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() { + 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 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 itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + 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() { + 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 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 itemInDb = client @@ -207,15 +204,8 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire assertThat(itemInDb.get("data").s()).isEqualTo("Fake Data"); } - - // - // ================================================================= - - // ================================================================= - // - @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); } - // - // ================================================================= - - // ================================================================= - // - @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 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 itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + 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() { + 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); @@ -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(); } - // - // ================================================================= - @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 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(); + Map 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 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 diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 7757be572..1ed628678 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -14,8 +14,8 @@ --> + 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"> 4.0.0 @@ -67,11 +67,6 @@ junit-jupiter-engine test - - org.mockito - mockito-core - test - org.slf4j slf4j-simple @@ -112,18 +107,17 @@ jsonassert test + + software.amazon.lambda + powertools-common + ${project.version} + test-jar + test + generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -131,7 +125,8 @@ maven-surefire-plugin 3.5.3 - -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 + -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 @@ -142,20 +137,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -173,29 +160,16 @@ --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun --initialize-at-build-time=org.slf4j.simple.SimpleLogger --initialize-at-build-time=org.slf4j.LoggerFactory --initialize-at-build-time=org.junit.Ignore --initialize-at-build-time=java.lang.annotation.Annotation --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 88e1ced39..ad0c3c373 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 @@ -39,6 +39,11 @@ org.apache.logging.log4j log4j-layout-template-json + + software.amazon.awssdk + sdk-core + provided + @@ -51,11 +56,6 @@ junit-jupiter-engine test - - org.mockito - mockito-core - test - org.junit-pioneer junit-pioneer @@ -91,18 +91,17 @@ jsonassert test + + software.amazon.lambda + powertools-common + ${project.version} + test-jar + test + generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -110,7 +109,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -121,20 +122,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -148,33 +141,17 @@ powertools-logging-log4j + + --initialize-at-build-time=org.junit.platform.launcher.core.DiscoveryIssueNotifier$1 + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces @@ -234,5 +211,4 @@ - diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/LoggingUserAgentInterceptor.java b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/LoggingUserAgentInterceptor.java new file mode 100644 index 000000000..a4d19363e --- /dev/null +++ b/powertools-logging/powertools-logging-log4j/src/main/java/software/amazon/lambda/powertools/logging/log4/internal/LoggingUserAgentInterceptor.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.log4.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 LoggingUserAgentInterceptor implements ExecutionInterceptor { + static { + UserAgentConfigurator.configureUserAgent("logging"); + } + + @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/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..7917018f2 --- /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.log4.internal.LoggingUserAgentInterceptor 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..470a8d728 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 powertools-parent @@ -49,8 +49,10 @@ test - org.mockito - mockito-core + software.amazon.lambda + powertools-common + ${project.version} + test-jar test @@ -88,14 +90,6 @@ generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -103,7 +97,12 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 + @@ -111,20 +110,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -136,37 +127,16 @@ - - true - Standard - powertools-logging-logback + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces @@ -227,6 +197,4 @@ - - 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":"","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/predefined-classes-config.json b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/predefined-classes-config.json new file mode 100644 index 000000000..0e79b2c5d --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/predefined-classes-config.json @@ -0,0 +1,8 @@ +[ + { + "type":"agent-extracted", + "classes":[ + ] + } +] + diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/proxy-config.json b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/proxy-config.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/proxy-config.json @@ -0,0 +1,2 @@ +[ +] 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..efd81020e 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,10 @@ [ +{ + "name":"[Ljava.lang.Object;" +}, +{ + "name":"[Ljava.lang.String;" +}, { "name":"ch.qos.logback.classic.joran.SerializedModelConfigurator", "methods":[{"name":"","parameterTypes":[] }] @@ -21,11 +27,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 +51,6 @@ "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "methods":[{"name":"","parameterTypes":[] }] }, -{ - "name":"com.sun.tools.attach.VirtualMachine" -}, -{ - "name":"java.io.FilePermission" -}, { "name":"java.io.IOException" }, @@ -66,153 +62,110 @@ }, { "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":"","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.util.Collection", + "queryAllDeclaredMethods":true }, { - "name":"java.lang.reflect.Method", - "methods":[{"name":"getAnnotatedReturnType","parameterTypes":[] }] + "name":"java.util.Collections$SingletonMap", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true }, { - "name":"java.lang.reflect.Parameter", - "methods":[{"name":"getModifiers","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isNamePresent","parameterTypes":[] }] + "name":"java.util.List", + "queryAllDeclaredMethods":true }, { - "name":"java.net.NetPermission" + "name":"java.util.Map", + "queryAllDeclaredMethods":true }, { - "name":"java.net.SocketPermission" + "name":"java.util.RandomAccess", + "queryAllDeclaredMethods":true }, { - "name":"java.net.URLPermission", - "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] }, { - "name":"java.security.AccessController", - "methods":[{"name":"doPrivileged","parameterTypes":["java.security.PrivilegedAction"] }, {"name":"doPrivileged","parameterTypes":["java.security.PrivilegedExceptionAction"] }] + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] }, { - "name":"java.security.AllPermission" + "name":"kotlin.Metadata" }, { - "name":"java.security.SecurityPermission" + "name":"org.apache.maven.surefire.junitplatform.JUnitPlatformProvider", + "methods":[{"name":"","parameterTypes":["org.apache.maven.surefire.api.provider.ProviderParameters"] }] }, { - "name":"java.util.PropertyPermission" + "name":"org.apiguardian.api.API", + "queryAllPublicMethods":true }, { - "name":"java.util.concurrent.ForkJoinTask", - "fields":[{"name":"aux"}, {"name":"status"}] + "name":"org.junit.internal.AssumptionViolatedException" }, { - "name":"java.util.concurrent.atomic.AtomicBoolean", - "fields":[{"name":"value"}] + "name":"org.junit.jupiter.api.ClassOrderer$OrderAnnotation", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"java.util.concurrent.atomic.AtomicReference", - "fields":[{"name":"value"}] + "name":"org.junit.jupiter.api.Order", + "queryAllPublicMethods":true }, { - "name":"javax.smartcardio.CardPermission" + "name":"org.junit.jupiter.api.Test", + "queryAllPublicMethods":true }, { - "name":"jdk.internal.misc.Unsafe" + "name":"org.junit.platform.commons.annotation.Testable", + "queryAllPublicMethods":true }, { - "name":"kotlin.jvm.JvmInline" + "name":"org.junit.platform.launcher.LauncherSession", + "methods":[{"name":"getLauncher","parameterTypes":[] }] }, { - "name":"org.apiguardian.api.API", - "queryAllPublicMethods":true + "name":"org.junit.platform.launcher.core.LauncherFactory", + "methods":[{"name":"openSession","parameterTypes":[] }] }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", @@ -282,9 +235,5 @@ "name":"software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder", "queryAllPublicMethods":true, "methods":[{"name":"","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..551635098 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 @@ -14,10 +14,24 @@ "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.commons.support.scanning.ClasspathScanner\\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":"\\QMETA-INF/services/software.amazon.lambda.powertools.logging.internal.LoggingManager\\E" + }, { + "pattern":"\\Qjunit-platform.properties\\E" }, { "pattern":"\\Qlogback-test.scmo\\E" }, { diff --git a/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/serialization-config.json b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/serialization-config.json new file mode 100644 index 000000000..fc967f7f8 --- /dev/null +++ b/powertools-logging/powertools-logging-logback/src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-logging-logback/serialization-config.json @@ -0,0 +1,11 @@ +{ + "types":[ + { + "name":"org.junit.platform.launcher.TestIdentifier$SerializedForm" + } + ], + "lambdaCapturingTypes":[ + ], + "proxies":[ + ] +} 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..398811d54 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}); + 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\":\"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 = "BobThe Sponge"; // 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 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 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 = "BobThe Sponge"; // 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..f2ec7dbc9 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -73,6 +73,11 @@ org.apache.commons commons-lang3 + + software.amazon.awssdk + sdk-core + provided + @@ -85,16 +90,6 @@ junit-jupiter-engine test - - org.mockito - mockito-core - test - - - org.mockito - mockito-junit-jupiter - test - org.slf4j slf4j-simple @@ -115,6 +110,13 @@ assertj-core test + + software.amazon.lambda + powertools-common + ${project.version} + test-jar + test + @@ -126,7 +128,8 @@ org.apache.maven.plugins maven-surefire-plugin - -Xlog:class+load=info:classesloaded.txt + + -Xlog:class+load=info:classesloaded.txt --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED @@ -137,20 +140,14 @@ generate-graalvm-files - - - org.mockito - mockito-subclass - test - - org.apache.maven.plugins maven-surefire-plugin - -Dmockito.mock.maker=subclass -Dorg.graalvm.nativeimage.imagecode=agent + + -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 +159,6 @@ graalvm-native - - - org.mockito - mockito-subclass - test - - @@ -191,40 +181,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces 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/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..544bd8ead --- /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 \ No newline at end of file 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, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -118,7 +118,7 @@ void builderShouldOverrideEnvironment() throws Exception { .build(); RequestHandler, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -141,7 +141,7 @@ void builderShouldOverrideEnvironment() throws Exception { void environmentVariablesShouldBeUsedWhenNoOverrides() throws Exception { // Given RequestHandler, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -166,7 +166,7 @@ void shouldUseDefaultsWhenNoConfiguration() throws Exception { .build(); RequestHandler, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map 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, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -98,7 +98,7 @@ void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { void shouldOverrideEnvironmentVariablesWithAnnotation() throws Exception { // Given RequestHandler, String> handler = new HandlerWithMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -120,7 +120,7 @@ void shouldOverrideEnvironmentVariablesWithAnnotation() throws Exception { void shouldUseEnvironmentVariablesWhenNoAnnotationOverrides() throws Exception { // Given RequestHandler, String> handler = new HandlerWithDefaultMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -140,7 +140,7 @@ void shouldUseEnvironmentVariablesWhenNoAnnotationOverrides() throws Exception { void shouldCaptureColdStartMetricWhenConfigured() throws Exception { // Given RequestHandler, String> handler = new HandlerWithColdStartMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map 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, String> handler = new HandlerWithColdStartMetricsAnnotation(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -195,7 +195,7 @@ void shouldNotIncludeServiceDimensionInColdStartMetricWhenServiceUndefined() thr void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exception { // Given RequestHandler, String> handler = new HandlerWithCustomFunctionName(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -228,7 +228,7 @@ void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exceptio void shouldUseServiceNameWhenProvidedForColdStartMetric() throws Exception { // Given RequestHandler, String> handler = new HandlerWithServiceNameAndColdStart(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When @@ -247,7 +247,7 @@ void shouldUseServiceNameWhenProvidedForColdStartMetric() throws Exception { void shouldHaveNoEffectOnNonHandlerMethod() { // Given RequestHandler, String> handler = new HandlerWithAnnotationOnWrongMethod(); - Context context = new TestContext(); + Context context = new TestLambdaContext(); Map input = new HashMap<>(); // When diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index d43221b5e..7e28ad556 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -14,8 +14,8 @@ --> + 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"> 4.0.0 @@ -43,11 +43,11 @@ software.amazon.awssdk url-connection-client - com.fasterxml.jackson.core jackson-databind + org.junit.jupiter @@ -93,14 +93,6 @@ generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -108,7 +100,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -119,20 +113,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -149,33 +135,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 4d650c58b..c32bb4d12 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 @@ -57,6 +57,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple @@ -86,7 +91,6 @@ org.mockito mockito-subclass - 5.18.0 test @@ -97,7 +101,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -112,7 +118,13 @@ org.mockito mockito-subclass - 5.18.0 + test + + + + + org.junit.jupiter + junit-jupiter-params test @@ -121,7 +133,7 @@ org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -138,33 +150,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces 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..c4ff00a38 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 @@ -58,6 +58,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple @@ -87,7 +92,6 @@ org.mockito mockito-subclass - 5.18.0 test @@ -98,7 +102,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -113,7 +119,13 @@ org.mockito mockito-subclass - 5.18.0 + test + + + + + org.junit.jupiter + junit-jupiter-params test @@ -122,7 +134,7 @@ org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -139,33 +151,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces 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 getItemValueCaptor; + @Captor ArgumentCaptor 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..cf262b8d6 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 @@ -58,6 +58,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple @@ -87,7 +92,6 @@ org.mockito mockito-subclass - 5.18.0 test @@ -98,7 +102,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -113,7 +119,13 @@ org.mockito mockito-subclass - 5.18.0 + test + + + + + org.junit.jupiter + junit-jupiter-params test @@ -122,7 +134,7 @@ org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -139,33 +151,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces 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..a418d3f09 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 @@ -58,6 +58,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple @@ -101,7 +106,6 @@ org.mockito mockito-subclass - 5.18.0 test @@ -112,7 +116,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -127,7 +133,13 @@ org.mockito mockito-subclass - 5.18.0 + test + + + + + org.junit.jupiter + junit-jupiter-params test @@ -136,7 +148,7 @@ org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -153,33 +165,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces @@ -187,4 +177,4 @@ - \ No newline at end of file + diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index abd2a02d2..5db059123 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -1,7 +1,7 @@ + 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"> 4.0.0 software.amazon.lambda @@ -50,6 +50,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple @@ -97,7 +102,6 @@ org.mockito mockito-subclass - 5.18.0 test @@ -108,7 +112,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -123,7 +129,13 @@ org.mockito mockito-subclass - 5.18.0 + test + + + + + org.junit.jupiter + junit-jupiter-params test @@ -132,7 +144,7 @@ org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -149,33 +161,11 @@ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback - -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun - --initialize-at-build-time=org.slf4j.simple.SimpleLogger - --initialize-at-build-time=org.slf4j.LoggerFactory - --initialize-at-build-time=org.junit.Ignore - --initialize-at-build-time=java.lang.annotation.Annotation - --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces 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 ssmParamCaptor; + @Captor ArgumentCaptor ssmParamByPathCaptor; + @Mock SecretsManagerClient secretsManagerClient; + @Captor ArgumentCaptor 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..b82a56af5 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -14,8 +14,8 @@ --> + 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"> 4.0.0 @@ -54,6 +54,11 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-engine + test + org.slf4j slf4j-simple @@ -93,14 +98,6 @@ generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -108,7 +105,9 @@ maven-surefire-plugin 3.5.3 - -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 + + -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 @@ -116,20 +115,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -141,37 +132,22 @@ - - true - Standard - powertools-serialization + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED --no-fallback -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun --initialize-at-build-time=org.slf4j.simple.SimpleLogger --initialize-at-build-time=org.slf4j.LoggerFactory --initialize-at-build-time=org.junit.Ignore --initialize-at-build-time=java.lang.annotation.Annotation --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index cb7f8218f..4d6f40511 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -14,8 +14,8 @@ --> + 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"> 4.0.0 powertools-tracing @@ -87,8 +87,10 @@ test - org.mockito - mockito-core + software.amazon.lambda + powertools-common + ${project.version} + test-jar test @@ -121,14 +123,6 @@ generate-graalvm-files - - - org.mockito - mockito-subclass - 5.18.0 - test - - @@ -136,7 +130,8 @@ maven-surefire-plugin 3.5.3 - -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 + -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 @@ -147,20 +142,12 @@ graalvm-native - - - org.mockito - mockito-subclass - 5.18.0 - test - - org.graalvm.buildtools native-maven-plugin - 0.11.0 + 0.11.0 true @@ -179,10 +166,6 @@ --enable-url-protocols=http --no-fallback -Dorg.graalvm.nativeimage.imagecode=agent - -H:ClassInitialization=net.bytebuddy.ClassFileVersion:rerun - -H:ClassInitialization=net.bytebuddy.utility.dispatcher.JavaDispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.Invoker$Dispatcher:rerun - -H:ClassInitialization=net.bytebuddy.utility.GraalImageCode:rerun -H:IncludeResources=version.properties -H:IncludeResources=unreadable.properties --initialize-at-build-time=org.slf4j.simple.SimpleLogger @@ -190,23 +173,11 @@ --initialize-at-build-time=org.junit.Ignore --initialize-at-build-time=java.lang.annotation.Annotation --initialize-at-build-time=org.junit.runners.model.FrameworkField - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1 - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2 - --initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase - --initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType - --initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod - --initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2 - - --trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable - --verbose --native-image-info -H:+UnlockExperimentalVMOptions -H:Log=registerResource:5 + -H:+ReportExceptionStackTraces 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); - } }