diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3722537ae..88f18ea29 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,9 @@ version: 2 updates: - + - package-ecosystem: "maven" + directory: "/aws-lambda-java-runtime-interface" + schedule: + interval: "weekly" - package-ecosystem: "github-actions" directory: "/" schedule: diff --git a/.github/workflows/aws-lambda-java-core.yml b/.github/workflows/aws-lambda-java-core.yml index c8064513c..3e4364672 100644 --- a/.github/workflows/aws-lambda-java-core.yml +++ b/.github/workflows/aws-lambda-java-core.yml @@ -1,9 +1,10 @@ # This workflow will be triggered if there will be changes to aws-lambda-java-core -# package and it builds the package and the packages that depend on it. +# package and it builds the package. name: Java CI aws-lambda-java-core on: + workflow_dispatch: push: branches: [ main ] paths: @@ -14,30 +15,22 @@ on: - 'aws-lambda-java-core/**' - '.github/workflows/aws-lambda-java-core.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto - - # Install base module + cache: maven + - name: Install core with Maven run: mvn -B install --file aws-lambda-java-core/pom.xml - - # Package modules that depend on base module - - name: Package log4j2 with Maven - run: mvn -B package --file aws-lambda-java-log4j2/pom.xml - - # Test Runtime Interface Client - - name: Run 'pr' target - working-directory: ./aws-lambda-java-runtime-interface-client - run: make pr - env: - IS_JAVA_8: true diff --git a/.github/workflows/aws-lambda-java-events-sdk-transformer.yml b/.github/workflows/aws-lambda-java-events-sdk-transformer.yml index 285848a9f..144d52f86 100644 --- a/.github/workflows/aws-lambda-java-events-sdk-transformer.yml +++ b/.github/workflows/aws-lambda-java-events-sdk-transformer.yml @@ -1,36 +1,43 @@ -# This workflow will be triggered if there will be changes to -# aws-lambda-java-events-sdk-transformer package and it builds the package. +# This workflow will be triggered if there will be changes to +# aws-lambda-java-events-sdk-transformer package or its dependency (events), +# and it builds the package. name: Java CI aws-lambda-java-events-sdk-transformer on: + workflow_dispatch: push: branches: [ main ] paths: - 'aws-lambda-java-events-sdk-transformer/**' + - 'aws-lambda-java-events/**' pull_request: branches: [ '*' ] paths: - 'aws-lambda-java-events-sdk-transformer/**' + - 'aws-lambda-java-events/**' - '.github/workflows/aws-lambda-java-events-sdk-transformer.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto + cache: maven - # Install base module + # Install dependency - name: Install events with Maven run: mvn -B install --file aws-lambda-java-events/pom.xml # Package target module - name: Package events-sdk-transformer with Maven run: mvn -B package --file aws-lambda-java-events-sdk-transformer/pom.xml - diff --git a/.github/workflows/aws-lambda-java-events.yml b/.github/workflows/aws-lambda-java-events.yml index b3b360b45..18be63cf9 100644 --- a/.github/workflows/aws-lambda-java-events.yml +++ b/.github/workflows/aws-lambda-java-events.yml @@ -1,9 +1,10 @@ # This workflow will be triggered if there will be changes to aws-lambda-java-events -# package and it builds the package and the packages that depend on it. +# package and it builds the package. name: Java CI aws-lambda-java-events on: + workflow_dispatch: push: branches: [ main ] paths: @@ -14,26 +15,22 @@ on: - 'aws-lambda-java-events/**' - '.github/workflows/aws-lambda-java-events.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto - - # Install base module + cache: maven + - name: Install events with Maven run: mvn -B install --file aws-lambda-java-events/pom.xml - - # Package modules that depend on base module - - name: Package serialization with Maven - run: mvn -B package --file aws-lambda-java-serialization/pom.xml - - name: Package events-sdk-transformer with Maven - run: mvn -B package --file aws-lambda-java-events-sdk-transformer/pom.xml - diff --git a/.github/workflows/aws-lambda-java-log4j2.yml b/.github/workflows/aws-lambda-java-log4j2.yml index 03718e602..945a1cb30 100644 --- a/.github/workflows/aws-lambda-java-log4j2.yml +++ b/.github/workflows/aws-lambda-java-log4j2.yml @@ -1,36 +1,42 @@ -# This workflow will be triggered if there will be changes to -# aws-lambda-java-log4j2 package and it builds the package. +# This workflow will be triggered if there will be changes to +# aws-lambda-java-log4j2 package or its dependency (core), and it builds the package. name: Java CI aws-lambda-java-log4j2 on: push: + workflow_dispatch: branches: [ main ] paths: - 'aws-lambda-java-log4j2/**' + - 'aws-lambda-java-core/**' pull_request: branches: [ '*' ] paths: - 'aws-lambda-java-log4j2/**' + - 'aws-lambda-java-core/**' - '.github/workflows/aws-lambda-java-log4j2.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto - - # Install base module + cache: maven + + # Install dependency - name: Install core with Maven run: mvn -B install --file aws-lambda-java-core/pom.xml # Package target module - name: Package log4j2 with Maven run: mvn -B package --file aws-lambda-java-log4j2/pom.xml - diff --git a/.github/workflows/aws-lambda-java-profiler.yml b/.github/workflows/aws-lambda-java-profiler.yml index 880320953..a098bfd14 100644 --- a/.github/workflows/aws-lambda-java-profiler.yml +++ b/.github/workflows/aws-lambda-java-profiler.yml @@ -22,16 +22,17 @@ jobs: contents: read steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: 21 distribution: corretto + cache: maven - name: Issue AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 with: aws-region: ${{ secrets.AWS_REGION_PROFILER_EXTENSION_INTEGRATION_TEST }} role-to-assume: ${{ secrets.AWS_ROLE_PROFILER_EXTENSION_INTEGRATION_TEST }} @@ -58,12 +59,16 @@ jobs: working-directory: ./experimental/aws-lambda-java-profiler run: ./integration_tests/invoke_function.sh + - name: Invoke Java Custom Options function + working-directory: ./experimental/aws-lambda-java-profiler + run: ./integration_tests/invoke_function_custom_options.sh + - name: Download from s3 working-directory: ./experimental/aws-lambda-java-profiler run: ./integration_tests/download_from_s3.sh - name: Upload profiles - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: profiles path: /tmp/s3-artifacts diff --git a/.github/workflows/aws-lambda-java-serialization.yml b/.github/workflows/aws-lambda-java-serialization.yml index b2700e088..f52c96fed 100644 --- a/.github/workflows/aws-lambda-java-serialization.yml +++ b/.github/workflows/aws-lambda-java-serialization.yml @@ -1,39 +1,46 @@ -# This workflow will be triggered if there will be changes to aws-lambda-java-serialization -# package and it builds the package and the packages that depend on it. +# This workflow will be triggered if there will be changes to aws-lambda-java-serialization +# package or its dependency (events), and it builds the package. name: Java CI aws-lambda-java-serialization on: + workflow_dispatch: push: branches: [ main ] paths: - 'aws-lambda-java-serialization/**' + - 'aws-lambda-java-events/**' pull_request: branches: [ '*' ] paths: - 'aws-lambda-java-serialization/**' + - 'aws-lambda-java-events/**' - '.github/workflows/aws-lambda-java-serialization.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto - - # Install base module + cache: maven + + # Install dependency - name: Install events with Maven run: mvn -B install --file aws-lambda-java-events/pom.xml # Package and install target module - name: Package serialization with Maven - run: mvn -B package install --file aws-lambda-java-serialization/pom.xml + run: mvn -B install --file aws-lambda-java-serialization/pom.xml # Run tests - name: Run tests from aws-lambda-java-tests diff --git a/.github/workflows/aws-lambda-java-tests.yml b/.github/workflows/aws-lambda-java-tests.yml index 1b818014a..324c44514 100644 --- a/.github/workflows/aws-lambda-java-tests.yml +++ b/.github/workflows/aws-lambda-java-tests.yml @@ -1,33 +1,42 @@ # This workflow will be triggered if there will be changes to aws-lambda-java-tests -# package and it builds the package and the packages that depend on it. +# package or its dependencies (events, serialization), and it builds the package. name: Java CI aws-lambda-java-tests on: + workflow_dispatch: push: branches: [ main ] paths: - 'aws-lambda-java-tests/**' + - 'aws-lambda-java-events/**' + - 'aws-lambda-java-serialization/**' pull_request: branches: [ '*' ] paths: - 'aws-lambda-java-tests/**' + - 'aws-lambda-java-events/**' + - 'aws-lambda-java-serialization/**' - '.github/workflows/aws-lambda-java-tests.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto - - # Install base module + cache: maven + + # Install dependencies - name: Install events with Maven run: mvn -B install --file aws-lambda-java-events/pom.xml - name: Install serialization with Maven @@ -36,4 +45,3 @@ jobs: # Package target module - name: Package tests with Maven run: mvn -B package --file aws-lambda-java-tests/pom.xml - diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index 300341c1f..4934754d8 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -9,6 +9,10 @@ on: - '.github/workflows/repo-sync.yml' workflow_dispatch: +permissions: + contents: write + pull-requests: write + jobs: repo-sync: name: Repo Sync @@ -16,9 +20,9 @@ jobs: env: IS_CONFIGURED: ${{ secrets.SOURCE_REPO != '' }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 if: ${{ env.IS_CONFIGURED == 'true' }} - - uses: repo-sync/github-sync@v2 + - uses: repo-sync/github-sync@3832fe8e2be32372e1b3970bbae8e7079edeec88 # v2.3.0 name: Sync repo to branch if: ${{ env.IS_CONFIGURED == 'true' }} with: @@ -26,7 +30,7 @@ jobs: source_branch: main destination_branch: ${{ secrets.INTERMEDIATE_BRANCH }} github_token: ${{ secrets.GITHUB_TOKEN }} - - uses: repo-sync/pull-request@v2 + - uses: repo-sync/pull-request@7e79a9f5dc3ad0ce53138f01df2fad14a04831c5 # v2.12.1 name: Create pull request if: ${{ env.IS_CONFIGURED == 'true' }} with: diff --git a/.github/workflows/runtime-interface-client_merge_to_main.yml b/.github/workflows/runtime-interface-client_merge_to_main.yml index 8909d56bf..d0d479111 100644 --- a/.github/workflows/runtime-interface-client_merge_to_main.yml +++ b/.github/workflows/runtime-interface-client_merge_to_main.yml @@ -11,12 +11,12 @@ name: Publish artifact for aws-lambda-java-runtime-interface-client on: + workflow_dispatch: push: branches: [ main ] paths: - 'aws-lambda-java-runtime-interface-client/**' - '.github/workflows/runtime-interface-client_*.yml' - workflow_dispatch: jobs: @@ -28,19 +28,20 @@ jobs: contents: read steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: 8 distribution: corretto + cache: maven - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 with: install: true @@ -49,7 +50,7 @@ jobs: - name: Build and install serialization dependency locally working-directory: ./aws-lambda-java-serialization - run: mvn clean install -DskipTests + run: mvn clean install - name: Test Runtime Interface Client xplatform build - Run 'build' target working-directory: ./aws-lambda-java-runtime-interface-client @@ -61,7 +62,7 @@ jobs: if: env.ENABLE_SNAPSHOT != null env: ENABLE_SNAPSHOT: ${{ secrets.ENABLE_SNAPSHOT }} - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 with: aws-region: ${{ secrets.AWS_REGION }} role-to-assume: ${{ secrets.AWS_ROLE }} @@ -90,6 +91,6 @@ jobs: - name: Upload coverage to Codecov if: env.CODECOV_TOKEN != null - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/runtime-interface-client_pr.yml b/.github/workflows/runtime-interface-client_pr.yml index 35c6ca06b..a0d8c6cc8 100644 --- a/.github/workflows/runtime-interface-client_pr.yml +++ b/.github/workflows/runtime-interface-client_pr.yml @@ -1,71 +1,80 @@ -# This workflow will be triggered if there will be changes to -# aws-lambda-java-runtime-interface-client package and it builds the package. +# This workflow will be triggered if there will be changes to +# aws-lambda-java-runtime-interface-client package or its dependencies (core, serialization), +# and it builds the package. name: PR to runtime-interface-client on: + workflow_dispatch: pull_request: branches: [ '*' ] paths: - 'aws-lambda-java-runtime-interface-client/**' + - 'aws-lambda-java-core/**' + - 'aws-lambda-java-serialization/**' - '.github/workflows/runtime-interface-client_*.yml' +permissions: + contents: read + jobs: smoke-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: 8 distribution: corretto - + cache: maven + - name: Build and install core dependency locally working-directory: ./aws-lambda-java-core run: mvn clean install - name: Build and install serialization dependency locally working-directory: ./aws-lambda-java-serialization - run: mvn clean install -DskipTests + run: mvn clean install - name: Runtime Interface Client smoke tests - Run 'pr' target working-directory: ./aws-lambda-java-runtime-interface-client run: make pr env: IS_JAVA_8: true - + build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: 8 distribution: corretto + cache: maven - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 with: install: true - name: Available buildx platforms run: echo ${{ steps.buildx.outputs.platforms }} - + - name: Build and install core dependency locally working-directory: ./aws-lambda-java-core run: mvn clean install - name: Build and install serialization dependency locally working-directory: ./aws-lambda-java-serialization - run: mvn clean install -DskipTests + run: mvn clean install - name: Test Runtime Interface Client xplatform build - Run 'build' target working-directory: ./aws-lambda-java-runtime-interface-client @@ -74,13 +83,13 @@ jobs: IS_JAVA_8: true - name: Save the built jar - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: aws-lambda-java-runtime-interface-client path: ./aws-lambda-java-runtime-interface-client/target/aws-lambda-java-runtime-interface-client-*.jar - name: Upload coverage to Codecov if: env.CODECOV_TOKEN != null - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/samples.yml b/.github/workflows/samples.yml index 2b5e7833f..68e25827d 100644 --- a/.github/workflows/samples.yml +++ b/.github/workflows/samples.yml @@ -1,37 +1,46 @@ -# This workflow will be triggered if there will be changes to aws-lambda-java-core -# package and it builds the package and the packages that depend on it. +# This workflow will be triggered if there will be changes to samples +# or their dependencies (events, serialization, tests). name: Java CI samples on: + workflow_dispatch: push: branches: [ main ] paths: - 'samples/**' + - 'aws-lambda-java-events/**' + - 'aws-lambda-java-serialization/**' + - 'aws-lambda-java-tests/**' pull_request: branches: [ '*' ] paths: - 'samples/**' + - 'aws-lambda-java-events/**' + - 'aws-lambda-java-serialization/**' + - 'aws-lambda-java-tests/**' - '.github/workflows/samples.yml' +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 1.8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 8 distribution: corretto + cache: maven - # Install events module + # Install dependencies - name: Install events with Maven run: mvn -B install --file aws-lambda-java-events/pom.xml - # Install serialization module - name: Install serialization with Maven run: mvn -B install --file aws-lambda-java-serialization/pom.xml - # Install tests module - name: Install tests with Maven run: mvn -B install --file aws-lambda-java-tests/pom.xml @@ -42,15 +51,16 @@ jobs: custom-serialization: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 # Set up both Java 8 and 21 - name: Set up Java 8 and 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: | 8 21 distribution: corretto + cache: maven # Install events module using Java 8 - name: Install events with Maven @@ -63,7 +73,7 @@ jobs: # Build custom-serialization samples - name: install sam - uses: aws-actions/setup-sam@v2 + uses: aws-actions/setup-sam@d78e1a4a9656d3b223e59b80676a797f20093133 # v2 - name: test fastJson run: cd samples/custom-serialization/fastJson && sam build && sam local invoke -e events/event.json | grep 200 - name: test gson diff --git a/.gitignore b/.gitignore index 9f99cc415..5a277e5d6 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ experimental/aws-lambda-java-profiler/extension/build/ experimental/aws-lambda-java-profiler/integration_tests/helloworld/bin !experimental/aws-lambda-java-profiler/extension/gradle/wrapper/*.jar /scratch/ +.vscode +.kiro +build +mise.toml diff --git a/README.md b/README.md index b5153a87f..580e14e41 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ The purpose of this package is to allow developers to deploy their applications com.amazonaws aws-lambda-java-runtime-interface-client - 2.7.0 + 2.10.1 ``` diff --git a/aws-lambda-java-events-sdk-transformer/pom.xml b/aws-lambda-java-events-sdk-transformer/pom.xml index 9b6afece1..6de599ef7 100644 --- a/aws-lambda-java-events-sdk-transformer/pom.xml +++ b/aws-lambda-java-events-sdk-transformer/pom.xml @@ -38,6 +38,8 @@ 1.8 1.11.914 2.15.40 + 5.12.2 + 3.5.4 @@ -70,7 +72,7 @@ org.junit.jupiter junit-jupiter-engine - 5.7.0 + ${junit-jupiter.version} test @@ -79,11 +81,14 @@ maven-surefire-plugin - 2.22.2 + ${maven-surefire-plugin.version} + + true + maven-failsafe-plugin - 2.22.2 + ${maven-surefire-plugin.version} @@ -160,18 +165,16 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - sonatype-nexus-staging - https://aws.oss.sonatype.org/ - false + central - + \ No newline at end of file diff --git a/aws-lambda-java-events/pom.xml b/aws-lambda-java-events/pom.xml index 925273e9b..c8c40e0c7 100644 --- a/aws-lambda-java-events/pom.xml +++ b/aws-lambda-java-events/pom.xml @@ -1,6 +1,6 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.amazonaws @@ -37,6 +37,9 @@ 1.18.22 UTF-8 UTF-8 + 2.20.1 + 2.40.1 + 5.12.2 @@ -56,19 +59,19 @@ org.junit.jupiter junit-jupiter-engine - 5.9.2 + ${junit-jupiter.version} test com.fasterxml.jackson.core jackson-databind - 2.14.2 + ${jackson.version} test net.javacrumbs.json-unit json-unit-assertj - 2.36.1 + ${json.unit} test @@ -152,20 +155,18 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - sonatype-nexus-staging - https://aws.oss.sonatype.org/ - false + central org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.1 UTF-8 @@ -173,7 +174,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.11.0 @@ -189,4 +190,4 @@ - + \ No newline at end of file diff --git a/aws-lambda-java-log4j2/README.md b/aws-lambda-java-log4j2/README.md index b1b739b69..f13121750 100644 --- a/aws-lambda-java-log4j2/README.md +++ b/aws-lambda-java-log4j2/README.md @@ -39,7 +39,7 @@ If using maven shade plugin, set the plugin configuration as follows org.apache.maven.plugins maven-shade-plugin - 2.4.3 + 3.6.1 package diff --git a/aws-lambda-java-log4j2/pom.xml b/aws-lambda-java-log4j2/pom.xml index b33300ef2..0124598a0 100644 --- a/aws-lambda-java-log4j2/pom.xml +++ b/aws-lambda-java-log4j2/pom.xml @@ -5,7 +5,7 @@ com.amazonaws aws-lambda-java-log4j2 - 1.6.0 + 1.6.1 jar AWS Lambda Java Log4j 2.x Libraries @@ -34,7 +34,7 @@ 1.8 1.8 - 2.17.1 + 2.25.3 @@ -134,18 +134,16 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - sonatype-nexus-staging - https://aws.oss.sonatype.org/ - false + central - + \ No newline at end of file diff --git a/aws-lambda-java-runtime-interface-client/README.md b/aws-lambda-java-runtime-interface-client/README.md index 67a5972d6..c448e7a89 100644 --- a/aws-lambda-java-runtime-interface-client/README.md +++ b/aws-lambda-java-runtime-interface-client/README.md @@ -70,7 +70,7 @@ pom.xml com.amazonaws aws-lambda-java-runtime-interface-client - 2.7.0 + 2.10.1 @@ -203,7 +203,7 @@ platform-specific JAR by setting the ``. com.amazonaws aws-lambda-java-runtime-interface-client - 2.7.0 + 2.10.1 linux-x86_64 ``` diff --git a/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md b/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md index ac073ae85..94a8a8057 100644 --- a/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md +++ b/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md @@ -1,3 +1,48 @@ +### March 19, 2026 +`2.10.1` +- Revert aws-lambda-java-serialization dependency to 1.2.0 + +### March 12, 2026 +`2.10.0` +- Update aws-lambda-java-serialization dependency to 1.3.0 + +### March 12, 2026 +`2.9.0` +- Update aws-lambda-java-serialization dependency to 1.2.0 + +### September 22, 2025 +`2.8.7` +- Remove Minimum and Maximum Limits of AWS_LAMBDA_MAX_CONCURRENCY. + +### September 22, 2025 +`2.8.6` +- Set Multiconcurrent Trace ID using utils-lite. + +### September 17, 2025 +`2.8.5` +- Log errorType and errorMessage from RAPID in C++ Client. +- Performance Upgrade for Multiconcurrency Mode. + +### September 9, 2025 +`2.8.4` +- Make Trace ID Accessible through Context Object. + +### July 19, 2025 +`2.8.3` +- Ensure EventHandlerLoader Thread Safety. + +### June 26, 2025 +`2.8.2` +- Allow AWS_LAMBDA_MAX_CONCURRENCY to be One. Crash the RIC if it is set to an un-parsable string to an integer or an out of bounds value. + +### June 26, 2025 +`2.8.1` +- Refactoring + +### June 26, 2025 +`2.8.0` +- Refactoring + ### May 21, 2025 `2.7.0` - Adding support for multi tenancy ([#540](https://github.com/aws/aws-lambda-java-libs/pull/540)) diff --git a/aws-lambda-java-runtime-interface-client/pom.xml b/aws-lambda-java-runtime-interface-client/pom.xml index c854fabcd..a09fd3df7 100644 --- a/aws-lambda-java-runtime-interface-client/pom.xml +++ b/aws-lambda-java-runtime-interface-client/pom.xml @@ -1,10 +1,10 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.4 + 2.10.1 jar AWS Lambda Java Runtime Interface Client @@ -37,8 +37,9 @@ 0.8.12 2.4 3.1.1 - 5.9.2 + 5.12.2 3.4.0 + 3.5.4 true - - - + + + + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.amazonaws aws-lambda-java-tests - 1.1.2 + 1.1.3 jar AWS Lambda Java Tests @@ -32,20 +33,29 @@ 1.8 1.8 UTF-8 + 5.9.2 0.8.7 + 1.4.0 + 3.16.1 + 3.18.0 + 3.27.7 com.amazonaws aws-lambda-java-serialization - 1.1.6 + ${aws-lambda-java-serialization.version} com.amazonaws aws-lambda-java-events - 3.16.1 + ${aws-lambda-java-events.version} org.junit.jupiter @@ -65,13 +75,13 @@ org.apache.commons commons-lang3 - 3.18.0 + ${commons-lang3.version} org.assertj assertj-core - 3.24.2 + ${assertj-core.version} test @@ -220,14 +230,12 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - sonatype-nexus-staging - https://aws.oss.sonatype.org/ - false + central @@ -240,7 +248,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.11.0 ${maven.compiler.source} ${maven.compiler.target} @@ -251,7 +259,10 @@ org.apache.maven.plugins maven-surefire-plugin 2.22.2 + + true + - + \ No newline at end of file diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtils.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtils.java new file mode 100644 index 000000000..f9f4e1eb6 --- /dev/null +++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtils.java @@ -0,0 +1,110 @@ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.Iterator; +import java.util.List; +import java.util.TreeSet; +import java.util.regex.Pattern; + +import org.joda.time.DateTime; + +/** + * Utility methods for working with shaded Jackson {@link JsonNode} trees. + * + *

+ * Package-private — not part of the public API. + *

+ */ +class JsonNodeUtils { + + private static final Pattern ISO_DATE_REGEX = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T.+"); + + private JsonNodeUtils() { + } + + /** + * Recursively removes all fields whose value is {@code null} from the + * tree. This mirrors the serializer's {@code Include.NON_NULL} behaviour + * so that explicit nulls in the fixture don't cause false-positive diffs. + */ + static JsonNode stripNulls(JsonNode node) { + if (node.isObject()) { + ObjectNode obj = (ObjectNode) node; + Iterator fieldNames = obj.fieldNames(); + while (fieldNames.hasNext()) { + String field = fieldNames.next(); + if (obj.get(field).isNull()) { + fieldNames.remove(); + } else { + stripNulls(obj.get(field)); + } + } + } else if (node.isArray()) { + for (JsonNode element : node) { + stripNulls(element); + } + } + return node; + } + + /** + * Recursively walks both trees and collects human-readable diff lines. + */ + static void diffNodes(String path, JsonNode expected, JsonNode actual, List diffs) { + if (expected.equals(actual)) + return; + + // Compares two datetime strings by parsed instant, because DateTimeModule + // normalizes the format on serialization (e.g. "+0000" → "Z", "Z" → ".000Z") + if (areSameDateTime(expected.textValue(), actual.textValue())) { + return; + } + + if (expected.isObject() && actual.isObject()) { + TreeSet allKeys = new TreeSet<>(); + expected.fieldNames().forEachRemaining(allKeys::add); + actual.fieldNames().forEachRemaining(allKeys::add); + for (String key : allKeys) { + diffChild(path + "." + key, expected.get(key), actual.get(key), diffs); + } + } else if (expected.isArray() && actual.isArray()) { + for (int i = 0; i < Math.max(expected.size(), actual.size()); i++) { + diffChild(path + "[" + i + "]", expected.get(i), actual.get(i), diffs); + } + } else { + diffs.add("CHANGED " + path + " : " + summarize(expected) + " -> " + summarize(actual)); + } + } + + /** + * Compares two strings by parsed instant when both look like ISO-8601 dates, + * because DateTimeModule normalizes format on serialization + * (e.g. "+0000" → "Z", "Z" → ".000Z"). + */ + private static boolean areSameDateTime(String expected, String actual) { + if (expected == null || actual == null + || !ISO_DATE_REGEX.matcher(expected).matches() + || !ISO_DATE_REGEX.matcher(actual).matches()) { + return false; + } + return DateTime.parse(expected).equals(DateTime.parse(actual)); + } + + private static void diffChild(String path, JsonNode expected, JsonNode actual, List diffs) { + if (expected == null) + diffs.add("ADDED " + path + " = " + summarize(actual)); + else if (actual == null) + diffs.add("MISSING " + path + " (was " + summarize(expected) + ")"); + else + diffNodes(path, expected, actual, diffs); + } + + private static String summarize(JsonNode node) { + if (node == null) { + return ""; + } + String text = node.toString(); + return text.length() > 80 ? text.substring(0, 77) + "..." : text; + } +} diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java new file mode 100644 index 000000000..f8d7e106d --- /dev/null +++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java @@ -0,0 +1,142 @@ +package com.amazonaws.services.lambda.runtime.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; +import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.ArrayList; +import java.util.List; + +/** + * Framework-agnostic assertion utilities for verifying Lambda event + * serialization. + * + *

+ * When opentest4j is on the classpath (e.g. JUnit 5.x / JUnit Platform), + * assertion failures are reported as + * {@code org.opentest4j.AssertionFailedError} + * which enables rich diff support in IDEs. Otherwise, falls back to plain + * {@link AssertionError}. + *

+ * + */ +public class LambdaEventAssert { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + /** + * Round-trip using the registered {@link LambdaEventSerializers} path + * (Jackson + mixins + DateModule + DateTimeModule + naming strategies). + * + *

+ * The check performs two consecutive round-trips + * (JSON → POJO → JSON → POJO → JSON) and compares the + * original JSON tree against the final output tree. A single structural + * comparison catches both: + *

+ *
    + *
  • Fields silently dropped during deserialization
  • + *
  • Non-idempotent serialization (output changes across round-trips)
  • + *
+ * + * @param fileName classpath resource name (must end with {@code .json}) + * @param targetClass the event class to deserialize into + * @throws AssertionError if the original and final JSON trees differ + */ + public static void assertSerializationRoundTrip(String fileName, Class targetClass) { + PojoSerializer serializer = LambdaEventSerializers.serializerFor(targetClass, + ClassLoader.getSystemClassLoader()); + + if (!fileName.endsWith(".json")) { + throw new IllegalArgumentException("File " + fileName + " must have json extension"); + } + + byte[] originalBytes; + try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { + if (stream == null) { + throw new IllegalArgumentException("Could not load resource '" + fileName + "' from classpath"); + } + originalBytes = toBytes(stream); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read resource " + fileName, e); + } + + // Two round-trips: original → POJO → JSON → POJO → JSON + // We are doing 2 passes so we can check instability problems + // like UnstablePojo in LambdaEventAssertTest + ByteArrayOutputStream firstOutput = roundTrip(new ByteArrayInputStream(originalBytes), serializer); + ByteArrayOutputStream secondOutput = roundTrip( + new ByteArrayInputStream(firstOutput.toByteArray()), serializer); + + // Compare original tree against final tree. + // Strip explicit nulls from the original because the serializer is + // configured with Include.NON_NULL — null fields are intentionally + // omitted and that is not a data-loss bug. + try { + JsonNode originalTree = JsonNodeUtils.stripNulls(MAPPER.readTree(originalBytes)); + JsonNode finalTree = MAPPER.readTree(secondOutput.toByteArray()); + + if (!originalTree.equals(finalTree)) { + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", originalTree, finalTree, diffs); + + if (!diffs.isEmpty()) { + StringBuilder msg = new StringBuilder(); + msg.append("Serialization round-trip failure for ") + .append(targetClass.getSimpleName()) + .append(" (").append(diffs.size()).append(" difference(s)):\n"); + for (String diff : diffs) { + msg.append(" ").append(diff).append('\n'); + } + + String expected = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(originalTree); + String actual = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(finalTree); + throw buildAssertionError(msg.toString(), expected, actual); + } + } + } catch (IOException e) { + throw new UncheckedIOException("Failed to parse JSON for tree comparison", e); + } + } + + private static ByteArrayOutputStream roundTrip(InputStream stream, PojoSerializer serializer) { + T event = serializer.fromJson(stream); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + serializer.toJson(event, outputStream); + return outputStream; + } + + private static byte[] toBytes(InputStream stream) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] chunk = new byte[4096]; + int n; + while ((n = stream.read(chunk)) != -1) { + buffer.write(chunk, 0, n); + } + return buffer.toByteArray(); + } + + /** + * Tries to create an opentest4j AssertionFailedError for rich IDE diff + * support. Falls back to plain AssertionError if opentest4j is not on + * the classpath. + */ + private static AssertionError buildAssertionError(String message, String expected, String actual) { + try { + // opentest4j is provided by JUnit Platform (5.x) and enables + // IDE diff viewers to show expected vs actual side-by-side. + Class cls = Class.forName("org.opentest4j.AssertionFailedError"); + return (AssertionError) cls + .getConstructor(String.class, Object.class, Object.class) + .newInstance(message, expected, actual); + } catch (ReflectiveOperationException e) { + return new AssertionError(message + "\nExpected:\n" + expected + "\nActual:\n" + actual); + } + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java index 752b84e27..43030bbca 100644 --- a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java @@ -81,6 +81,12 @@ public void testLoadAPIGatewayV2CustomAuthorizerEvent() { assertThat(event).isNotNull(); assertThat(event.getRequestContext().getHttp().getMethod()).isEqualTo("POST"); + // getTime() converts the raw string "12/Mar/2020:19:03:58 +0000" into a DateTime object; + // Jackson then serializes it as ISO-8601 "2020-03-12T19:03:58.000Z" + assertThat(event.getRequestContext().getTime().toInstant().getMillis()) + .isEqualTo(DateTime.parse("2020-03-12T19:03:58.000Z").toInstant().getMillis()); + // getTimeEpoch() converts the raw long into an Instant; + // Jackson then serializes it as a decimal seconds value assertThat(event.getRequestContext().getTimeEpoch()).isEqualTo(Instant.ofEpochMilli(1583348638390L)); } @@ -136,6 +142,9 @@ public void testLoadLexEvent() { assertThat(event.getCurrentIntent().getName()).isEqualTo("BookHotel"); assertThat(event.getCurrentIntent().getSlots()).hasSize(4); assertThat(event.getBot().getName()).isEqualTo("BookTrip"); + // Jackson leniently coerces the JSON number for "Nights" into a String + // because slots is typed as Map + assertThat(event.getCurrentIntent().getSlots().get("Nights")).isInstanceOf(String.class); } @Test @@ -159,6 +168,10 @@ public void testLoadMSKFirehoseEvent() { assertThat(event.getRecords().get(0).getKafkaRecordValue().array()).asString().isEqualTo("{\"Name\":\"Hello World\"}"); assertThat(event.getRecords().get(0).getApproximateArrivalTimestamp()).asString().isEqualTo("1716369573887"); assertThat(event.getRecords().get(0).getMskRecordMetadata()).asString().isEqualTo("{offset=0, partitionId=1, approximateArrivalTimestamp=1716369573887}"); + // Jackson leniently coerces the JSON number in mskRecordMetadata into a String + // because the map is typed as Map + Map metadata = event.getRecords().get(0).getMskRecordMetadata(); + assertThat(metadata.get("approximateArrivalTimestamp")).isInstanceOf(String.class); } @Test @@ -408,6 +421,8 @@ public void testLoadRabbitMQEvent() { .returns("AIDACKCEVSQ6C2EXAMPLE", from(RabbitMQEvent.BasicProperties::getUserId)) .returns(80, from(RabbitMQEvent.BasicProperties::getBodySize)) .returns("Jan 1, 1970, 12:33:41 AM", from(RabbitMQEvent.BasicProperties::getTimestamp)); + // Jackson leniently coerces the JSON string "60000" for expiration into int + // because the model field is typed as int Map headers = basicProperties.getHeaders(); assertThat(headers).hasSize(3); diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtilsTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtilsTest.java new file mode 100644 index 000000000..ec5798dca --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtilsTest.java @@ -0,0 +1,155 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class JsonNodeUtilsTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + // --- stripNulls --- + + @Test + void stripNulls_removesTopLevelNulls() throws Exception { + JsonNode node = MAPPER.readTree("{\"a\":1,\"b\":null,\"c\":\"hello\"}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"a\":1,\"c\":\"hello\"}"), result); + } + + @Test + void stripNulls_removesNestedNulls() throws Exception { + JsonNode node = MAPPER.readTree("{\"outer\":{\"keep\":true,\"drop\":null}}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"outer\":{\"keep\":true}}"), result); + } + + @Test + void stripNulls_leavesArrayElementsIntact() throws Exception { + // Nulls inside arrays are kept (they're positional) + JsonNode node = MAPPER.readTree("{\"arr\":[1,null,3]}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"arr\":[1,null,3]}"), result); + } + + @Test + void stripNulls_removesNullsInsideArrayObjects() throws Exception { + JsonNode node = MAPPER.readTree("[{\"a\":1,\"b\":null},{\"c\":null}]"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("[{\"a\":1},{}]"), result); + } + + @Test + void stripNulls_noOpOnCleanTree() throws Exception { + JsonNode node = MAPPER.readTree("{\"a\":1,\"b\":\"two\"}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"a\":1,\"b\":\"two\"}"), result); + } + + // --- diffNodes --- + + @Test + void diffNodes_identicalTrees_noDiffs() throws Exception { + JsonNode a = MAPPER.readTree("{\"x\":1,\"y\":\"hello\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", a, a.deepCopy(), diffs); + assertTrue(diffs.isEmpty()); + } + + @Test + void diffNodes_changedValue() throws Exception { + JsonNode expected = MAPPER.readTree("{\"x\":1}"); + JsonNode actual = MAPPER.readTree("{\"x\":2}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).startsWith("CHANGED .x")); + } + + @Test + void diffNodes_missingField() throws Exception { + JsonNode expected = MAPPER.readTree("{\"a\":1,\"b\":2}"); + JsonNode actual = MAPPER.readTree("{\"a\":1}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains("MISSING") && diffs.get(0).contains(".b"), "got: " + diffs.get(0)); + } + + @Test + void diffNodes_addedField() throws Exception { + JsonNode expected = MAPPER.readTree("{\"a\":1}"); + JsonNode actual = MAPPER.readTree("{\"a\":1,\"b\":2}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size(), "diffs: " + diffs); + assertTrue(diffs.get(0).contains("ADDED") && diffs.get(0).contains(".b"), "got: " + diffs.get(0)); + } + + @Test + void diffNodes_nestedObjectDiff() throws Exception { + JsonNode expected = MAPPER.readTree("{\"outer\":{\"inner\":\"old\"}}"); + JsonNode actual = MAPPER.readTree("{\"outer\":{\"inner\":\"new\"}}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains(".outer.inner")); + } + + @Test + void diffNodes_arrayElementDiff() throws Exception { + JsonNode expected = MAPPER.readTree("{\"arr\":[1,2,3]}"); + JsonNode actual = MAPPER.readTree("{\"arr\":[1,99,3]}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains("[1]")); + } + + @Test + void diffNodes_arrayLengthMismatch() throws Exception { + JsonNode expected = MAPPER.readTree("{\"arr\":[1,2]}"); + JsonNode actual = MAPPER.readTree("{\"arr\":[1,2,3]}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains("ADDED"), "got: " + diffs.get(0)); + } + + // --- areSameDateTime (tested indirectly via diffNodes) --- + + @Test + void diffNodes_equivalentDateTimes_noDiff() throws Exception { + // "+0000" vs "Z" — same instant, different format + JsonNode expected = MAPPER.readTree("{\"t\":\"2020-03-12T19:03:58.000+0000\"}"); + JsonNode actual = MAPPER.readTree("{\"t\":\"2020-03-12T19:03:58.000Z\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertTrue(diffs.isEmpty(), "Expected no diffs for equivalent datetimes, got: " + diffs); + } + + @Test + void diffNodes_differentDateTimes_hasDiff() throws Exception { + JsonNode expected = MAPPER.readTree("{\"t\":\"2020-03-12T19:03:58.000Z\"}"); + JsonNode actual = MAPPER.readTree("{\"t\":\"2021-01-01T00:00:00.000Z\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + } + + @Test + void diffNodes_nonDateStrings_notTreatedAsDates() throws Exception { + JsonNode expected = MAPPER.readTree("{\"s\":\"hello\"}"); + JsonNode actual = MAPPER.readTree("{\"s\":\"world\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).startsWith("CHANGED .s")); + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssertTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssertTest.java new file mode 100644 index 000000000..63067f8b2 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssertTest.java @@ -0,0 +1,71 @@ +package com.amazonaws.services.lambda.runtime.tests; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class LambdaEventAssertTest { + /** + * Demonstrates the completeness check: the fixture has a field + * ({@code unknownField}) that {@code PartialPojo} does not capture, + * so it gets silently dropped during deserialization. + */ + @Test + void shouldFailWhenFieldIsDropped() { + AssertionError error = assertThrows(AssertionError.class, + () -> LambdaEventAssert.assertSerializationRoundTrip( + "partial_pojo.json", PartialPojo.class)); + + assertTrue(error.getMessage().contains("PartialPojo"), + "Error message should name the failing class"); + } + + /** + * Demonstrates the stability check: the getter mutates state on each + * call, so the first and second round-trips produce different JSON. + */ + @Test + void shouldFailWhenSerializationIsUnstable() { + AssertionError error = assertThrows(AssertionError.class, + () -> LambdaEventAssert.assertSerializationRoundTrip( + "unstable_pojo.json", UnstablePojo.class)); + + assertTrue(error.getMessage().contains("UnstablePojo"), + "Error message should name the failing class"); + } + + /** POJO that only captures {@code name}, silently dropping any other fields. */ + public static class PartialPojo { + private String name; + + public PartialPojo() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * POJO with a getter that appends a suffix, making serialization + * non-idempotent. + */ + public static class UnstablePojo { + private String name; + + public UnstablePojo() { + } + + public String getName() { + return name == null ? null : name + "_x"; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/ResponseEventSerializationRoundTripTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/ResponseEventSerializationRoundTripTest.java new file mode 100644 index 000000000..e700b0d01 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/ResponseEventSerializationRoundTripTest.java @@ -0,0 +1,59 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.services.lambda.runtime.events.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +/** + * Verifies serialization round-trip fidelity for Lambda response event types. + * + *

Response events are POJOs that Lambda functions return to the RIC, which + * serializes them to JSON via {@code EventHandlerLoader.getSerializer()}. + * None of these types are registered in {@code SUPPORTED_EVENTS}, so they + * go through the bare Jackson path ({@code JacksonFactory.getInstance() + * .getSerializer(type)}).

+ * + *

Although the RIC only calls {@code toJson()} on response types (never + * {@code fromJson()}), the round-trip test is a stricter check: if a response + * type survives JSON → POJO → JSON → POJO → JSON, it + * certainly survives the production POJO → JSON path.

+ * + * @see SerializationRoundTripTest for registered input events + * @see UnregisteredEventSerializationRoundTripTest for unregistered input events + */ +@SuppressWarnings("deprecation") // APIGatewayV2ProxyResponseEvent is deprecated +public class ResponseEventSerializationRoundTripTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("passingCases") + void roundTrip(String displayName, String fixture, Class eventClass) { + LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass); + } + + private static Stream passingCases() { + return Stream.of( + // API Gateway responses + args(APIGatewayProxyResponseEvent.class, "response/apigw_proxy_response.json"), + args(APIGatewayV2HTTPResponse.class, "response/apigw_v2_http_response.json"), + args(APIGatewayV2WebSocketResponse.class, "response/apigw_v2_websocket_response.json"), + args(APIGatewayV2ProxyResponseEvent.class, "response/apigw_v2_websocket_response.json"), + // ALB response + args(ApplicationLoadBalancerResponseEvent.class, "response/alb_response.json"), + // S3 Batch response + args(S3BatchResponse.class, "response/s3_batch_response.json"), + // SQS Batch response + args(SQSBatchResponse.class, "response/sqs_batch_response.json"), + // Simple IAM Policy response (HTTP API) + args(SimpleIAMPolicyResponse.class, "response/simple_iam_policy_response.json"), + // MSK Firehose response + args(MSKFirehoseResponse.class, "response/msk_firehose_response.json")); + } + + private static Arguments args(Class clazz, String fixture) { + return Arguments.of(clazz.getSimpleName(), fixture, clazz); + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java new file mode 100644 index 000000000..3eab8fec0 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java @@ -0,0 +1,108 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.services.lambda.runtime.events.*; +import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Verifies serialization round-trip fidelity for events that are registered in + * {@code LambdaEventSerializers.SUPPORTED_EVENTS}. + * + *

Registered events go through the full customized serialization path in the + * Runtime Interface Client (RIC): {@code EventHandlerLoader.getSerializer()} + * detects them via {@code isLambdaSupportedEvent()} and delegates to + * {@code LambdaEventSerializers.serializerFor()}, which applies Jackson mixins, + * {@code DateModule}/{@code DateTimeModule}, and naming strategies.

+ * + *

Each case feeds a JSON fixture through + * {@link LambdaEventAssert#assertSerializationRoundTrip} which performs two + * consecutive round-trips and compares the original JSON tree against the + * final output.

+ * + * @see UnregisteredEventSerializationRoundTripTest for events not in SUPPORTED_EVENTS + */ +public class SerializationRoundTripTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("passingCases") + void roundTrip(String displayName, String fixture, Class eventClass) { + LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass); + } + + @ParameterizedTest(name = "{0} (known failure)") + @MethodSource("knownFailureCases") + void roundTripKnownFailures(String displayName, String fixture, Class eventClass) { + assertThrows(Throwable.class, + () -> LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass), + displayName + " was expected to fail but passed — move it to passingCases()"); + } + + private static Stream passingCases() { + return Stream.of( + args(CloudFormationCustomResourceEvent.class, "cloudformation_event.json"), + args(CloudWatchLogsEvent.class, "cloudwatchlogs_event.json"), + args(CodeCommitEvent.class, "codecommit_event.json"), + args(ConfigEvent.class, "config_event.json"), + args(DynamodbEvent.class, "ddb/dynamo_event_roundtrip.json"), + args(KinesisEvent.class, "kinesis/kinesis_event_roundtrip.json"), + args(KinesisFirehoseEvent.class, "firehose_event.json"), + args(LambdaDestinationEvent.class, "lambda_destination_event.json"), + args(ScheduledEvent.class, "cloudwatch_event.json"), + args(SecretsManagerRotationEvent.class, "secrets_rotation_event.json"), + args(SNSEvent.class, "sns_event.json"), + args(LexEvent.class, "lex_event_roundtrip.json"), + args(ConnectEvent.class, "connect_event.json"), + args(SQSEvent.class, "sqs/sqs_event_nobody.json"), + args(APIGatewayProxyRequestEvent.class, "apigw_rest_event.json"), + args(CloudFrontEvent.class, "cloudfront_event.json"), + args(S3Event.class, "s3_event.json"), + args(S3EventNotification.class, "s3_event.json"), + args(APIGatewayV2HTTPEvent.class, "apigw_http_event.json"), + args(APIGatewayCustomAuthorizerEvent.class, "apigw_auth.json"), + args(ApplicationLoadBalancerRequestEvent.class, "elb_event.json"), + args(CloudWatchCompositeAlarmEvent.class, "cloudwatch_composite_alarm.json"), + args(CloudWatchMetricAlarmEvent.class, "cloudwatch_metric_alarm.json"), + args(CognitoUserPoolPreTokenGenerationEventV2.class, "cognito_user_pool_pre_token_generation_event_v2.json"), + args(KafkaEvent.class, "kafka_event_roundtrip.json"), + args(MSKFirehoseEvent.class, "msk_firehose_event_roundtrip.json"), + args(RabbitMQEvent.class, "rabbitmq_event_roundtrip.json"), + args(S3BatchEventV2.class, "s3_batch_event_v2.json"), + args(IoTButtonEvent.class, "iot_button_event.json"), + args(CognitoEvent.class, "cognito_sync_event.json"), + args(DynamodbTimeWindowEvent.class, "ddb/dynamo_time_window_event.json"), + args(KinesisTimeWindowEvent.class, "kinesis/kinesis_time_window_event.json")); + } + + private static Stream knownFailureCases() { + return Stream.of( + // APIGatewayV2CustomAuthorizerEvent has two serialization issues: + // 1. getTime() parses the raw string "12/Mar/2020:19:03:58 +0000" into a + // DateTime via dd/MMM/yyyy formatter. Jackson serializes as ISO-8601, but + // the formatter cannot parse ISO-8601 back on the second round-trip. + // The time field is effectively mandatory (getTime() throws NPE if null), + // and the date format change is inherent to how the serialization works. + // 2. getTimeEpoch() converts long to Instant; Jackson serializes as decimal + // seconds (e.g. 1583348638.390000000) instead of the original long. + // Both transformations are lossy; coercion captured in EventLoaderTest. + args(APIGatewayV2CustomAuthorizerEvent.class, "apigw_auth_v2.json"), + // ActiveMQEvent has one serialization issue: + // Destination.physicalName (camelCase) vs JSON "physicalname" (lowercase) — + // ACCEPT_CASE_INSENSITIVE_PROPERTIES is disabled in JacksonFactory so the + // field is silently dropped during deserialization. + // Fix: create an ActiveMQEventMixin with a DestinationMixin that maps + // @JsonProperty("physicalname") to getPhysicalName()/setPhysicalName(), + // then register it in LambdaEventSerializers MIXIN_MAP and NESTED_CLASS_MAP. + args(ActiveMQEvent.class, "mq_event.json")); + } + + private static Arguments args(Class clazz, String fixture) { + return Arguments.of(clazz.getSimpleName(), fixture, clazz); + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/UnregisteredEventSerializationRoundTripTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/UnregisteredEventSerializationRoundTripTest.java new file mode 100644 index 000000000..12ad818e4 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/UnregisteredEventSerializationRoundTripTest.java @@ -0,0 +1,90 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.services.lambda.runtime.events.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Verifies serialization round-trip fidelity for events that are NOT registered + * in {@code LambdaEventSerializers.SUPPORTED_EVENTS}. + * + *

In the Runtime Interface Client (RIC), when a handler's event type is not + * in {@code SUPPORTED_EVENTS}, {@code EventHandlerLoader.getSerializer()} falls + * through to {@code JacksonFactory.getInstance().getSerializer(type)} — a bare + * {@code PojoSerializer} backed by Jackson without any mixins or naming + * strategies. However, {@code LambdaEventSerializers.serializerFor()} (used by + * this test) unconditionally registers {@code DateModule} and + * {@code DateTimeModule}, so Joda/java.time types are still handled. For most + * unregistered events this makes no practical difference because they don't + * contain Joda DateTime fields.

+ * + * @see SerializationRoundTripTest for events registered in SUPPORTED_EVENTS + */ +@SuppressWarnings("deprecation") // APIGatewayV2ProxyRequestEvent is deprecated +public class UnregisteredEventSerializationRoundTripTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("passingCases") + void roundTrip(String displayName, String fixture, Class eventClass) { + LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass); + } + + @ParameterizedTest(name = "{0} (known failure)") + @MethodSource("knownFailureCases") + void roundTripKnownFailures(String displayName, String fixture, Class eventClass) { + assertThrows(Throwable.class, + () -> LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass), + displayName + " was expected to fail but passed — move it to passingCases()"); + } + + private static Stream passingCases() { + return Stream.of( + // S3 Batch + args(S3BatchEvent.class, "s3_batch_event.json"), + // AppSync + args(AppSyncLambdaAuthorizerEvent.class, "appsync_authorizer_event.json"), + args(AppSyncLambdaAuthorizerResponse.class, "appsync_authorizer_response.json"), + // TimeWindow response + args(TimeWindowEventResponse.class, "time_window_event_response.json"), + // Cognito UserPool triggers + args(CognitoUserPoolPreSignUpEvent.class, "cognito/cognito_userpool_presignup.json"), + args(CognitoUserPoolPostConfirmationEvent.class, "cognito/cognito_userpool_postconfirmation.json"), + args(CognitoUserPoolPreAuthenticationEvent.class, "cognito/cognito_userpool_preauthentication.json"), + args(CognitoUserPoolPostAuthenticationEvent.class, "cognito/cognito_userpool_postauthentication.json"), + args(CognitoUserPoolDefineAuthChallengeEvent.class, "cognito/cognito_userpool_define_auth_challenge.json"), + args(CognitoUserPoolCreateAuthChallengeEvent.class, "cognito/cognito_userpool_create_auth_challenge.json"), + args(CognitoUserPoolVerifyAuthChallengeResponseEvent.class, "cognito/cognito_userpool_verify_auth_challenge.json"), + args(CognitoUserPoolMigrateUserEvent.class, "cognito/cognito_userpool_migrate_user.json"), + args(CognitoUserPoolCustomMessageEvent.class, "cognito/cognito_userpool_custom_message.json"), + args(CognitoUserPoolPreTokenGenerationEvent.class, "cognito/cognito_userpool_pre_token_generation.json"), + // Kinesis Analytics + args(KinesisAnalyticsFirehoseInputPreprocessingEvent.class, "kinesis/kinesis_analytics_firehose_input_preprocessing.json"), + args(KinesisAnalyticsStreamsInputPreprocessingEvent.class, "kinesis/kinesis_analytics_streams_input_preprocessing.json"), + args(KinesisAnalyticsInputPreprocessingResponse.class, "kinesis/kinesis_analytics_input_preprocessing_response.json"), + args(KinesisAnalyticsOutputDeliveryEvent.class, "kinesis/kinesis_analytics_output_delivery.json"), + args(KinesisAnalyticsOutputDeliveryResponse.class, "kinesis/kinesis_analytics_output_delivery_response.json"), + // API Gateway V2 WebSocket + args(APIGatewayV2WebSocketEvent.class, "apigw_websocket_event.json"), + args(APIGatewayV2ProxyRequestEvent.class, "apigw_websocket_event.json")); + } + + private static Stream knownFailureCases() { + return Stream.of( + // S3ObjectLambdaEvent: Lombok generates getXAmzRequestId() for field + // "xAmzRequestId". With USE_STD_BEAN_NAMING, Jackson derives the property + // name as "XAmzRequestId" (capital X), so the original "xAmzRequestId" key + // is silently dropped during deserialization. + // Fix: add @JsonProperty("xAmzRequestId") on the field or getter. + args(S3ObjectLambdaEvent.class, "s3_object_lambda_event.json")); + } + + private static Arguments args(Class clazz, String fixture) { + return Arguments.of(clazz.getSimpleName(), fixture, clazz); + } +} diff --git a/aws-lambda-java-tests/src/test/resources/apigw_http_event.json b/aws-lambda-java-tests/src/test/resources/apigw_http_event.json index 88f4e5b4b..91954656c 100644 --- a/aws-lambda-java-tests/src/test/resources/apigw_http_event.json +++ b/aws-lambda-java-tests/src/test/resources/apigw_http_event.json @@ -18,18 +18,6 @@ "requestContext": { "accountId": "123456789012", "apiId": "api-id", - "authentication": { - "clientCert": { - "clientCertPem": "CERT_CONTENT", - "subjectDN": "www.example.com", - "issuerDN": "Example issuer", - "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", - "validity": { - "notBefore": "May 28 12:30:02 2019 GMT", - "notAfter": "Aug 5 09:36:04 2021 GMT" - } - } - }, "authorizer": { "jwt": { "claims": { diff --git a/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json b/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json index 28f10c221..a139ccbe8 100644 --- a/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json +++ b/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json @@ -14,19 +14,22 @@ "Header2": [ "value1", "value2" + ], + "Header3": [ + "value1,value2" ] }, "queryStringParameters": { "parameter1": "value1", - "parameter2": "value" + "parameter2": "value2" }, "multiValueQueryStringParameters": { "parameter1": [ - "value1", - "value2" + "value1" ], "parameter2": [ - "value" + "value1", + "value2" ] }, "requestContext": { @@ -52,17 +55,7 @@ "sourceIp": "IP", "user": null, "userAgent": "user-agent", - "userArn": null, - "clientCert": { - "clientCertPem": "CERT_CONTENT", - "subjectDN": "www.example.com", - "issuerDN": "Example issuer", - "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", - "validity": { - "notBefore": "May 28 12:30:02 2019 GMT", - "notAfter": "Aug 5 09:36:04 2021 GMT" - } - } + "userArn": null }, "path": "/my/path", "protocol": "HTTP/1.1", @@ -76,5 +69,5 @@ "pathParameters": null, "stageVariables": null, "body": "Hello from Lambda!", - "isBase64Encoded": true + "isBase64Encoded": false } \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/apigw_websocket_event.json b/aws-lambda-java-tests/src/test/resources/apigw_websocket_event.json new file mode 100644 index 000000000..a47fc3a94 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/apigw_websocket_event.json @@ -0,0 +1,88 @@ +{ + "resource": "/", + "path": "/", + "httpMethod": "GET", + "headers": { + "Host": "abcdef1234.execute-api.us-east-1.amazonaws.com", + "Sec-WebSocket-Extensions": "permessage-deflate", + "Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==", + "Sec-WebSocket-Version": "13", + "X-Forwarded-For": "192.0.2.1", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Host": [ + "abcdef1234.execute-api.us-east-1.amazonaws.com" + ], + "Sec-WebSocket-Extensions": [ + "permessage-deflate" + ], + "Sec-WebSocket-Key": [ + "dGhlIHNhbXBsZSBub25jZQ==" + ], + "Sec-WebSocket-Version": [ + "13" + ], + "X-Forwarded-For": [ + "192.0.2.1" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": { + "param1": "value1" + }, + "multiValueQueryStringParameters": { + "param1": [ + "value1" + ] + }, + "pathParameters": { + "proxy": "path/to/resource" + }, + "stageVariables": { + "stageVar1": "value1" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "abcdef", + "stage": "prod", + "requestId": "abc-def-ghi", + "identity": { + "cognitoIdentityPoolId": "us-east-1:id-pool", + "accountId": "123456789012", + "cognitoIdentityId": "us-east-1:identity-id", + "caller": "caller-id", + "apiKey": "api-key-id", + "sourceIp": "192.0.2.1", + "cognitoAuthenticationType": "authenticated", + "cognitoAuthenticationProvider": "provider", + "userArn": "arn:aws:iam::123456789012:user/testuser", + "userAgent": "Mozilla/5.0", + "user": "testuser", + "accessKey": "AKIAIOSFODNN7EXAMPLE" + }, + "resourcePath": "/", + "httpMethod": "GET", + "apiId": "abcdef1234", + "connectedAt": 1583348638390, + "connectionId": "abc123=", + "domainName": "abcdef1234.execute-api.us-east-1.amazonaws.com", + "eventType": "CONNECT", + "extendedRequestId": "abc123=", + "integrationLatency": "100", + "messageDirection": "IN", + "messageId": "msg-001", + "requestTime": "09/Apr/2020:18:03:58 +0000", + "requestTimeEpoch": 1583348638390, + "routeKey": "$connect", + "status": "200" + }, + "body": "request body", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/appsync_authorizer_event.json b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_event.json new file mode 100644 index 000000000..494a500f3 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_event.json @@ -0,0 +1,15 @@ +{ + "authorizationToken": "BE9DC5E3-D410-4733-AF76-70178092E681", + "requestContext": { + "apiId": "giy7kumfmvcqvbedntjwjvagii", + "accountId": "254688921111", + "requestId": "b80ed838-14c6-4500-b4c3-b694c7bef086", + "queryDocument": "mutation MyNewTask($desc: String!) {\n createTask(description: $desc) {\n id\n }\n}\n", + "operationName": "MyNewTask", + "variables": {} + }, + "requestHeaders": { + "host": "giy7kumfmvcqvbedntjwjvagii.appsync-api.us-east-1.amazonaws.com", + "content-type": "application/json" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/appsync_authorizer_response.json b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_response.json new file mode 100644 index 000000000..1216ad51d --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_response.json @@ -0,0 +1,11 @@ +{ + "isAuthorized": true, + "resolverContext": { + "name": "Foo Man", + "balance": "100" + }, + "deniedFields": [ + "Mutation.createEvent" + ], + "ttlOverride": 15 +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cloudfront_event.json b/aws-lambda-java-tests/src/test/resources/cloudfront_event.json index 7485310e9..bf4625d06 100644 --- a/aws-lambda-java-tests/src/test/resources/cloudfront_event.json +++ b/aws-lambda-java-tests/src/test/resources/cloudfront_event.json @@ -7,7 +7,6 @@ }, "request": { "uri": "/test", - "querystring": "auth=test&foo=bar", "method": "GET", "clientIp": "2001:cdba::3257:9652", "headers": { diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_create_auth_challenge.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_create_auth_challenge.json new file mode 100644 index 000000000..495b41475 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_create_auth_challenge.json @@ -0,0 +1,37 @@ +{ + "version": "1", + "triggerSource": "CreateAuthChallenge_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "challengeName": "CUSTOM_CHALLENGE", + "session": [ + { + "challengeName": "PASSWORD_VERIFIER", + "challengeResult": true, + "challengeMetadata": "metadata1" + } + ], + "userNotFound": false + }, + "response": { + "publicChallengeParameters": { + "captchaUrl": "url/123.jpg" + }, + "privateChallengeParameters": { + "answer": "5" + }, + "challengeMetadata": "CAPTCHA_CHALLENGE" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_custom_message.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_custom_message.json new file mode 100644 index 000000000..aa7a53a83 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_custom_message.json @@ -0,0 +1,28 @@ +{ + "version": "1", + "triggerSource": "CustomMessage_SignUp", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com", + "phone_number_verified": "true", + "email_verified": "true" + }, + "clientMetadata": { + "meta1": "value1" + }, + "codeParameter": "####", + "usernameParameter": "testuser" + }, + "response": { + "smsMessage": "Your code is ####", + "emailMessage": "Your code is ####", + "emailSubject": "Welcome" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_define_auth_challenge.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_define_auth_challenge.json new file mode 100644 index 000000000..e320b71d1 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_define_auth_challenge.json @@ -0,0 +1,32 @@ +{ + "version": "1", + "triggerSource": "DefineAuthChallenge_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "session": [ + { + "challengeName": "PASSWORD_VERIFIER", + "challengeResult": true, + "challengeMetadata": "metadata1" + } + ], + "userNotFound": false + }, + "response": { + "challengeName": "CUSTOM_CHALLENGE", + "issueTokens": false, + "failAuthentication": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_migrate_user.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_migrate_user.json new file mode 100644 index 000000000..2897ae063 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_migrate_user.json @@ -0,0 +1,35 @@ +{ + "version": "1", + "triggerSource": "UserMigration_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "validationData": { + "key1": "val1" + }, + "clientMetadata": { + "meta1": "value1" + }, + "userName": "testuser", + "password": "test-password" + }, + "response": { + "userAttributes": { + "email": "user@example.com" + }, + "finalUserStatus": "CONFIRMED", + "messageAction": "SUPPRESS", + "desiredDeliveryMediums": [ + "EMAIL" + ], + "forceAliasCreation": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postauthentication.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postauthentication.json new file mode 100644 index 000000000..f41084d54 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postauthentication.json @@ -0,0 +1,20 @@ +{ + "version": "1", + "triggerSource": "PostAuthentication_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "newDeviceUsed": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postconfirmation.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postconfirmation.json new file mode 100644 index 000000000..ecf63c7d3 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postconfirmation.json @@ -0,0 +1,20 @@ +{ + "version": "1", + "triggerSource": "PostConfirmation_ConfirmSignUp", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com", + "email_verified": "true" + }, + "clientMetadata": { + "meta1": "value1" + } + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_pre_token_generation.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_pre_token_generation.json new file mode 100644 index 000000000..f81ffb902 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_pre_token_generation.json @@ -0,0 +1,48 @@ +{ + "version": "1", + "triggerSource": "TokenGeneration_HostedAuth", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "groupConfiguration": { + "groupsToOverride": [ + "group1", + "group2" + ], + "iamRolesToOverride": [ + "arn:aws:iam::123456789012:role/role1" + ], + "preferredRole": "arn:aws:iam::123456789012:role/role1" + } + }, + "response": { + "claimsOverrideDetails": { + "claimsToAddOrOverride": { + "custom:myattr": "myvalue" + }, + "claimsToSuppress": [ + "email" + ], + "groupOverrideDetails": { + "groupsToOverride": [ + "group1" + ], + "iamRolesToOverride": [ + "arn:aws:iam::123456789012:role/role1" + ], + "preferredRole": "arn:aws:iam::123456789012:role/role1" + } + } + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_preauthentication.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_preauthentication.json new file mode 100644 index 000000000..1402c4684 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_preauthentication.json @@ -0,0 +1,20 @@ +{ + "version": "1", + "triggerSource": "PreAuthentication_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "validationData": { + "key1": "val1" + }, + "userNotFound": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_presignup.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_presignup.json new file mode 100644 index 000000000..0d1f0936a --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_presignup.json @@ -0,0 +1,27 @@ +{ + "version": "1", + "triggerSource": "PreSignUp_SignUp", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "validationData": { + "key1": "val1" + }, + "clientMetadata": { + "meta1": "value1" + } + }, + "response": { + "autoConfirmUser": false, + "autoVerifyPhone": false, + "autoVerifyEmail": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_verify_auth_challenge.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_verify_auth_challenge.json new file mode 100644 index 000000000..ef14c4ddf --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_verify_auth_challenge.json @@ -0,0 +1,27 @@ +{ + "version": "1", + "triggerSource": "VerifyAuthChallengeResponse_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "privateChallengeParameters": { + "answer": "5" + }, + "challengeAnswer": "5", + "userNotFound": false + }, + "response": { + "answerCorrect": true + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito_sync_event.json b/aws-lambda-java-tests/src/test/resources/cognito_sync_event.json new file mode 100644 index 000000000..6edf1c246 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito_sync_event.json @@ -0,0 +1,20 @@ +{ + "version": 2, + "eventType": "SyncTrigger", + "region": "us-east-1", + "identityPoolId": "us-east-1:example-identity-pool-id", + "identityId": "us-east-1:example-identity-id", + "datasetName": "SampleDataset", + "datasetRecords": { + "SampleKey1": { + "oldValue": "oldValue1", + "newValue": "newValue1", + "op": "replace" + }, + "SampleKey2": { + "oldValue": "oldValue2", + "newValue": "newValue2", + "op": "replace" + } + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/connect_event.json b/aws-lambda-java-tests/src/test/resources/connect_event.json index b71bf6692..4ce17a657 100644 --- a/aws-lambda-java-tests/src/test/resources/connect_event.json +++ b/aws-lambda-java-tests/src/test/resources/connect_event.json @@ -12,15 +12,6 @@ "InitialContactId": "6ca32fbd-8f92-46af-92a5-6b0f970f0efe", "InitiationMethod": "API", "InstanceARN": "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa", - "MediaStreams": { - "Customer": { - "Audio": { - "StartFragmentNumber": "91343852333181432392682062622220590765191907586", - "StartTimestamp": "1565781909613", - "StreamARN": "arn:aws:kinesisvideo:eu-central-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806" - } - } - }, "PreviousContactId": "4ca32fbd-8f92-46af-92a5-6b0f970f0efe", "Queue": { "Name": "SampleQueue", diff --git a/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event_roundtrip.json new file mode 100644 index 000000000..10d963c3c --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event_roundtrip.json @@ -0,0 +1,97 @@ +{ + "Records": [ + { + "eventID": "c4ca4238a0b923820dcc509a6f75849b", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1.4285376E9, + "SequenceNumber": "4421584500000000017450439091", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "userIdentity": { + "principalId": "dynamodb.amazonaws.com", + "type": "Service" + } + }, + { + "eventID": "c81e728d9d4c2f636f067f89cc14862c", + "eventName": "MODIFY", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1.635734407123E9, + "SequenceNumber": "4421584500000000017450439092", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "eventName": "REMOVE", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1.4285376E9, + "SequenceNumber": "4421584500000000017450439093", + "SizeBytes": 38, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/ddb/dynamo_time_window_event.json b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_time_window_event.json new file mode 100644 index 000000000..d931acb80 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_time_window_event.json @@ -0,0 +1,75 @@ +{ + "Records": [ + { + "eventID": "1", + "eventName": "INSERT", + "eventVersion": "1.0", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "SequenceNumber": "111", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "2", + "eventName": "MODIFY", + "eventVersion": "1.0", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "SequenceNumber": "222", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + } + ], + "window": { + "start": "2020-07-30T17:00:00Z", + "end": "2020-07-30T17:05:00Z" + }, + "state": { + "1": "state1" + }, + "shardId": "shard123456789", + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "isFinalInvokeForWindow": false, + "isWindowTerminatedEarly": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/iot_button_event.json b/aws-lambda-java-tests/src/test/resources/iot_button_event.json new file mode 100644 index 000000000..8dc82826b --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/iot_button_event.json @@ -0,0 +1,5 @@ +{ + "serialNumber": "G030JF055364XVRB", + "clickType": "SINGLE", + "batteryVoltage": "2000mV" +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kafka_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/kafka_event_roundtrip.json new file mode 100644 index 000000000..d9f682e5f --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kafka_event_roundtrip.json @@ -0,0 +1,22 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-3432434/4834-3547-3455-9872-7929", + "bootstrapServers": "b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092", + "records": { + "mytopic-01": [ + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1596480920837, + "timestampType": "CREATE_TIME", + "value": "SGVsbG8gZnJvbSBLYWZrYSAhIQ==", + "headers": [ + { + "headerKey": "aGVhZGVyVmFsdWU=" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_firehose_input_preprocessing.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_firehose_input_preprocessing.json new file mode 100644 index 000000000..8c6cfe514 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_firehose_input_preprocessing.json @@ -0,0 +1,14 @@ +{ + "invocationId": "invocationIdExample", + "applicationArn": "arn:aws:kinesisanalytics:us-east-1:123456789012:application/my-app", + "streamArn": "arn:aws:firehose:us-east-1:123456789012:deliverystream/my-stream", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "kinesisFirehoseRecordMetadata": { + "approximateArrivalTimestamp": 1583348638390 + }, + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_input_preprocessing_response.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_input_preprocessing_response.json new file mode 100644 index 000000000..f5be190ec --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_input_preprocessing_response.json @@ -0,0 +1,9 @@ +{ + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "result": "Ok", + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery.json new file mode 100644 index 000000000..573b6baba --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery.json @@ -0,0 +1,13 @@ +{ + "invocationId": "invocationIdExample", + "applicationArn": "arn:aws:kinesisanalytics:us-east-1:123456789012:application/my-app", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "lambdaDeliveryRecordMetadata": { + "retryHint": 0 + }, + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery_response.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery_response.json new file mode 100644 index 000000000..56ddaa194 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery_response.json @@ -0,0 +1,8 @@ +{ + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "result": "Ok" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_streams_input_preprocessing.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_streams_input_preprocessing.json new file mode 100644 index 000000000..4ae8f3705 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_streams_input_preprocessing.json @@ -0,0 +1,17 @@ +{ + "invocationId": "invocationIdExample", + "applicationArn": "arn:aws:kinesisanalytics:us-east-1:123456789012:application/my-app", + "streamArn": "arn:aws:kinesis:us-east-1:123456789012:stream/my-stream", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "kinesisStreamRecordMetadata": { + "sequenceNumber": "49546986683135544286507457936321625675700192471156785154", + "partitionKey": "partKey", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1583348638390 + }, + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_event_roundtrip.json new file mode 100644 index 000000000..e2081ef2b --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_event_roundtrip.json @@ -0,0 +1,21 @@ +{ + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1.4285376E9, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_time_window_event.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_time_window_event.json new file mode 100644 index 000000000..2d6283c58 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_time_window_event.json @@ -0,0 +1,32 @@ +{ + "Records": [ + { + "kinesis": { + "kinesisSchemaVersion": "1.0", + "partitionKey": "1", + "sequenceNumber": "49590338271490256608559692538361571095921575989136588898", + "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==", + "approximateArrivalTimestamp": 1.607497475E9 + }, + "eventSource": "aws:kinesis", + "eventVersion": "1.0", + "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898", + "eventName": "aws:kinesis:record", + "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-kinesis-role", + "awsRegion": "us-east-1", + "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream" + } + ], + "window": { + "start": "2020-12-09T07:04:00Z", + "end": "2020-12-09T07:06:00Z" + }, + "state": { + "1": "282", + "2": "715" + }, + "shardId": "shardId-000000000006", + "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream", + "isFinalInvokeForWindow": false, + "isWindowTerminatedEarly": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/lex_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/lex_event_roundtrip.json new file mode 100644 index 000000000..be065eb3f --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/lex_event_roundtrip.json @@ -0,0 +1,24 @@ +{ + "messageVersion": "1.0", + "invocationSource": "DialogCodeHook", + "userId": "John", + "sessionAttributes": { + "key": "value" + }, + "bot": { + "name": "BookTrip", + "alias": "$LATEST", + "version": "$LATEST" + }, + "outputDialogMode": "Text", + "currentIntent": { + "name": "BookHotel", + "slots": { + "Location": "Chicago", + "CheckInDate": "2030-11-08", + "Nights": "4", + "RoomType": "queen" + }, + "confirmationStatus": "None" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/mq_event.json b/aws-lambda-java-tests/src/test/resources/mq_event.json index 6505a22d4..8b12af72e 100644 --- a/aws-lambda-java-tests/src/test/resources/mq_event.json +++ b/aws-lambda-java-tests/src/test/resources/mq_event.json @@ -5,15 +5,17 @@ { "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1", "messageType": "jms/text-message", - "data": "QUJDOkFBQUE=", - "connectionId": "myJMSCoID", + "timestamp": 1598827811958, + "deliveryMode": 0, "redelivered": false, + "expiration": 0, + "priority": 0, + "data": "QUJDOkFBQUE=", + "brokerInTime": 1598827811958, + "brokerOutTime": 1598827811959, "destination": { "physicalname": "testQueue" }, - "timestamp": 1598827811958, - "brokerInTime": 1598827811958, - "brokerOutTime": 1598827811959, "properties": { "testKey": "testValue" } @@ -21,15 +23,17 @@ { "messageID": "ID:b-8bcfa572-428a-4642-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1", "messageType": "jms/bytes-message", + "timestamp": 1598827811958, + "deliveryMode": 0, + "redelivered": false, + "expiration": 0, + "priority": 0, "data": "3DTOOW7crj51prgVLQaGQ82S48k=", - "connectionId": "myJMSCoID1", - "persistent": false, + "brokerInTime": 1598827811958, + "brokerOutTime": 1598827811959, "destination": { "physicalname": "testQueue" }, - "timestamp": 1598827811958, - "brokerInTime": 1598827811958, - "brokerOutTime": 1598827811959, "properties": { "testKey": "testValue" } diff --git a/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json b/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json index 6b839912d..140908250 100644 --- a/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json +++ b/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json @@ -15,4 +15,4 @@ "kafkaRecordValue": "eyJOYW1lIjoiSGVsbG8gV29ybGQifQ==" } ] -} +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/msk_firehose_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/msk_firehose_event_roundtrip.json new file mode 100644 index 000000000..81b0a9c81 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/msk_firehose_event_roundtrip.json @@ -0,0 +1,18 @@ +{ + "invocationId": "12345621-4787-0000-a418-36e56Example", + "sourceMSKArn": "arn:aws:kafka:EXAMPLE", + "deliveryStreamArn": "arn:aws:firehose:EXAMPLE", + "region": "us-east-1", + "records": [ + { + "recordId": "00000000000000000000000000000000000000000000000000000000000000", + "approximateArrivalTimestamp": 1716369573887, + "mskRecordMetadata": { + "offset": "0", + "partitionId": "1", + "approximateArrivalTimestamp": "1716369573887" + }, + "kafkaRecordValue": "eyJOYW1lIjoiSGVsbG8gV29ybGQifQ==" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/partial_pojo.json b/aws-lambda-java-tests/src/test/resources/partial_pojo.json new file mode 100644 index 000000000..398218039 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/partial_pojo.json @@ -0,0 +1,4 @@ +{ + "name": "test", + "unknownField": "this will be dropped" +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/rabbitmq_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/rabbitmq_event_roundtrip.json new file mode 100644 index 000000000..44edf2f0a --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/rabbitmq_event_roundtrip.json @@ -0,0 +1,51 @@ +{ + "eventSource": "aws:rmq", + "eventSourceArn": "arn:aws:mq:us-west-2:112556298976:broker:test:b-9bcfa592-423a-4942-879d-eb284b418fc8", + "rmqMessagesByQueue": { + "test::/": [ + { + "basicProperties": { + "contentType": "text/plain", + "contentEncoding": null, + "headers": { + "header1": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 49 + ] + }, + "header2": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 50 + ] + }, + "numberInHeader": 10 + }, + "deliveryMode": 1, + "priority": 34, + "correlationId": null, + "replyTo": null, + "expiration": 60000, + "messageId": null, + "timestamp": "Jan 1, 1970, 12:33:41 AM", + "type": null, + "userId": "AIDACKCEVSQ6C2EXAMPLE", + "appId": null, + "clusterId": null, + "bodySize": 80 + }, + "redelivered": false, + "data": "eyJ0aW1lb3V0IjowLCJkYXRhIjoiQ1pybWYwR3c4T3Y0YnFMUXhENEUifQ==" + } + ] + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/alb_response.json b/aws-lambda-java-tests/src/test/resources/response/alb_response.json new file mode 100644 index 000000000..355eb193a --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/alb_response.json @@ -0,0 +1,15 @@ +{ + "statusCode": 200, + "statusDescription": "200 OK", + "headers": { + "Content-Type": "text/html" + }, + "multiValueHeaders": { + "Set-Cookie": [ + "cookie1=value1", + "cookie2=value2" + ] + }, + "body": "Hello", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/apigw_proxy_response.json b/aws-lambda-java-tests/src/test/resources/response/apigw_proxy_response.json new file mode 100644 index 000000000..640ccdc5c --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/apigw_proxy_response.json @@ -0,0 +1,15 @@ +{ + "statusCode": 200, + "headers": { + "Content-Type": "application/json", + "X-Custom-Header": "custom-value" + }, + "multiValueHeaders": { + "Set-Cookie": [ + "cookie1=value1", + "cookie2=value2" + ] + }, + "body": "{\"message\":\"Hello from Lambda\"}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/apigw_v2_http_response.json b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_http_response.json new file mode 100644 index 000000000..c39236650 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_http_response.json @@ -0,0 +1,16 @@ +{ + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "multiValueHeaders": { + "Set-Cookie": [ + "cookie1=value1" + ] + }, + "cookies": [ + "session=abc123; Secure; HttpOnly" + ], + "body": "{\"message\":\"OK\"}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/apigw_v2_websocket_response.json b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_websocket_response.json new file mode 100644 index 000000000..08392e890 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_websocket_response.json @@ -0,0 +1,14 @@ +{ + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "multiValueHeaders": { + "X-Custom": [ + "val1", + "val2" + ] + }, + "body": "{\"action\":\"sendmessage\",\"data\":\"hello\"}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/msk_firehose_response.json b/aws-lambda-java-tests/src/test/resources/response/msk_firehose_response.json new file mode 100644 index 000000000..9ac497624 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/msk_firehose_response.json @@ -0,0 +1,9 @@ +{ + "records": [ + { + "recordId": "record-1", + "result": "Ok", + "kafkaRecordValue": "dHJhbnNmb3JtZWQgZGF0YQ==" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/s3_batch_response.json b/aws-lambda-java-tests/src/test/resources/response/s3_batch_response.json new file mode 100644 index 000000000..e63439d84 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/s3_batch_response.json @@ -0,0 +1,12 @@ +{ + "invocationSchemaVersion": "1.0", + "treatMissingKeysAs": "PermanentFailure", + "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo", + "results": [ + { + "taskId": "dGFza2lkZ29lc2hlcmUK", + "resultCode": "Succeeded", + "resultString": "Successfully processed" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/simple_iam_policy_response.json b/aws-lambda-java-tests/src/test/resources/response/simple_iam_policy_response.json new file mode 100644 index 000000000..5f23b6405 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/simple_iam_policy_response.json @@ -0,0 +1,7 @@ +{ + "isAuthorized": true, + "context": { + "userId": "user-123", + "scope": "read:all" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/sqs_batch_response.json b/aws-lambda-java-tests/src/test/resources/response/sqs_batch_response.json new file mode 100644 index 000000000..5ef2de697 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/sqs_batch_response.json @@ -0,0 +1,7 @@ +{ + "batchItemFailures": [ + { + "itemIdentifier": "059f36b4-87a3-44ab-83d2-661975830a7d" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/s3_batch_event.json b/aws-lambda-java-tests/src/test/resources/s3_batch_event.json new file mode 100644 index 000000000..a70af0fdd --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/s3_batch_event.json @@ -0,0 +1,15 @@ +{ + "invocationSchemaVersion": "1.0", + "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo", + "job": { + "id": "f3cc4f60-61f6-4a2b-8a21-d07600c373ce" + }, + "tasks": [ + { + "taskId": "dGFza2lkZ29lc2hlcmUK", + "s3Key": "customerImage1.jpg", + "s3VersionId": "1", + "s3BucketArn": "arn:aws:s3:::amzn-s3-demo-bucket" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/s3_event.json b/aws-lambda-java-tests/src/test/resources/s3_event.json index 89e0bd312..73f59d072 100644 --- a/aws-lambda-java-tests/src/test/resources/s3_event.json +++ b/aws-lambda-java-tests/src/test/resources/s3_event.json @@ -28,8 +28,10 @@ }, "object": { "key": "test/key", + "urlDecodedKey": "test/key", "size": 1024, "eTag": "0123456789abcdef0123456789abcdef", + "versionId": "", "sequencer": "0A1B2C3D4E5F678901" } } diff --git a/aws-lambda-java-tests/src/test/resources/s3_object_lambda_event.json b/aws-lambda-java-tests/src/test/resources/s3_object_lambda_event.json new file mode 100644 index 000000000..db996e71c --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/s3_object_lambda_event.json @@ -0,0 +1,29 @@ +{ + "xAmzRequestId": "requestId", + "getObjectContext": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=snip", + "outputRoute": "io-use1-001", + "outputToken": "OutputToken" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "AssumedRole", + "principalId": "principalId", + "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example", + "accountId": "111122223333", + "accessKeyId": "accessKeyId" + }, + "protocolVersion": "1.00" +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/time_window_event_response.json b/aws-lambda-java-tests/src/test/resources/time_window_event_response.json new file mode 100644 index 000000000..3c77b3784 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/time_window_event_response.json @@ -0,0 +1,10 @@ +{ + "state": { + "totalAmount": "500" + }, + "batchItemFailures": [ + { + "itemIdentifier": "49590338271490256608559692538361571095921575989136588898" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/unstable_pojo.json b/aws-lambda-java-tests/src/test/resources/unstable_pojo.json new file mode 100644 index 000000000..19db9a1cc --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/unstable_pojo.json @@ -0,0 +1,3 @@ +{ + "name": "test" +} \ No newline at end of file diff --git a/experimental/aws-lambda-java-profiler/README.md b/experimental/aws-lambda-java-profiler/README.md index ccc66399e..c15c22791 100644 --- a/experimental/aws-lambda-java-profiler/README.md +++ b/experimental/aws-lambda-java-profiler/README.md @@ -83,6 +83,25 @@ When the agent is constructed, it starts the profiler and registers itself as a A new thread is created to handle calling `/next` and uploading the results of the profiler to S3. The bucket to upload the result to is configurable using an environment variable. +### Custom Parameters for the Profiler + +Users can configure the profiler output by setting environment variables. + +``` +# Example: Output as JFR format instead of HTML +AWS_LAMBDA_PROFILER_START_COMMAND="start,event=wall,interval=1us,file=/tmp/profile.jfr" +AWS_LAMBDA_PROFILER_STOP_COMMAND="stop,file=%s" +``` + +Defaults are the following: + +``` +AWS_LAMBDA_PROFILER_START_COMMAND="start,event=wall,interval=1us" +AWS_LAMBDA_PROFILER_STOP_COMMAND="stop,file=%s,include=*AWSLambda.main,include=start_thread" +``` + +See [async-profiler's ProfilerOptions](https://github.com/async-profiler/async-profiler/blob/master/docs/ProfilerOptions.md) for all available profiler parameters. + ### Troubleshooting - Ensure the Lambda function execution role has the necessary permissions to write to the S3 bucket. diff --git a/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml b/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml index 01bbf0d67..4b46f4e2b 100644 --- a/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml +++ b/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml @@ -11,7 +11,7 @@ UTF-8 2.155.0 [10.0.0,11.0.0) - 5.7.1 + 5.12.2 diff --git a/experimental/aws-lambda-java-profiler/examples/function/profiling-example/pom.xml b/experimental/aws-lambda-java-profiler/examples/function/profiling-example/pom.xml index c7465bfd8..ac1001009 100644 --- a/experimental/aws-lambda-java-profiler/examples/function/profiling-example/pom.xml +++ b/experimental/aws-lambda-java-profiler/examples/function/profiling-example/pom.xml @@ -46,7 +46,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.1 diff --git a/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/Constants.java b/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/Constants.java new file mode 100644 index 000000000..f9ca3010c --- /dev/null +++ b/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/Constants.java @@ -0,0 +1,29 @@ +package com.amazonaws.services.lambda.extension; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Constants { + + private static final String DEFAULT_AWS_LAMBDA_PROFILER_START_COMMAND = + "start,event=wall,interval=1us"; + private static final String DEFAULT_AWS_LAMBDA_PROFILER_STOP_COMMAND = + "stop,file=%s,include=*AWSLambda.main,include=start_thread"; + public static final String PROFILER_START_COMMAND = + System.getenv().getOrDefault( + "AWS_LAMBDA_PROFILER_START_COMMAND", + DEFAULT_AWS_LAMBDA_PROFILER_START_COMMAND + ); + public static final String PROFILER_STOP_COMMAND = + System.getenv().getOrDefault( + "AWS_LAMBDA_PROFILER_STOP_COMMAND", + DEFAULT_AWS_LAMBDA_PROFILER_STOP_COMMAND + ); + + public static String getFilePathFromEnv(){ + Pattern pattern = Pattern.compile("file=([^,]+)"); + Matcher matcher = pattern.matcher(PROFILER_START_COMMAND); + + return matcher.find() ? matcher.group(1) : "/tmp/profiling-data-%s.html"; + } +} diff --git a/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/PreMain.java b/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/PreMain.java index c0522641a..2a84eb641 100644 --- a/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/PreMain.java +++ b/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/PreMain.java @@ -2,49 +2,57 @@ // SPDX-License-Identifier: MIT-0 package com.amazonaws.services.lambda.extension; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.lang.instrument.Instrumentation; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; +import one.profiler.AsyncProfiler; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; +import static com.amazonaws.services.lambda.extension.Constants.PROFILER_START_COMMAND; +import static com.amazonaws.services.lambda.extension.Constants.PROFILER_STOP_COMMAND; + +public class PreMain { -import one.profiler.AsyncProfiler; -public class PreMain { + private static final String INTERNAL_COMMUNICATION_PORT = + System.getenv().getOrDefault( + "AWS_LAMBDA_PROFILER_COMMUNICATION_PORT", + "1234" + ); - private static final String DEFAULT_AWS_LAMBDA_PROFILER_START_COMMAND = "start,event=wall,interval=1us"; - private static final String DEFAULT_AWS_LAMBDA_PROFILER_STOP_COMMAND = "stop,file=%s,include=*AWSLambda.main,include=start_thread"; - private static final String PROFILER_START_COMMAND = System.getenv().getOrDefault("AWS_LAMBDA_PROFILER_START_COMMAND", DEFAULT_AWS_LAMBDA_PROFILER_START_COMMAND); - private static final String PROFILER_STOP_COMMAND = System.getenv().getOrDefault("AWS_LAMBDA_PROFILER_STOP_COMMAND", DEFAULT_AWS_LAMBDA_PROFILER_STOP_COMMAND); - private static final String INTERNAL_COMMUNICATION_PORT = System.getenv().getOrDefault("AWS_LAMBDA_PROFILER_COMMUNICATION_PORT", "1234"); + + private String filepath; public static void premain(String agentArgs, Instrumentation inst) { Logger.debug("premain is starting"); - if(!createFileIfNotExist("/tmp/aws-lambda-java-profiler")) { + if (!createFileIfNotExist("/tmp/aws-lambda-java-profiler")) { Logger.debug("starting the profiler for coldstart"); startProfiler(); registerShutdownHook(); try { Integer port = Integer.parseInt(INTERNAL_COMMUNICATION_PORT); Logger.debug("using profile communication port = " + port); - HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); + HttpServer server = HttpServer.create( + new InetSocketAddress(port), + 0 + ); server.createContext("/profiler/start", new StartProfiler()); server.createContext("/profiler/stop", new StopProfiler()); server.setExecutor(null); // Use the default executor server.start(); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); } } } private static boolean createFileIfNotExist(String filePath) { - File file = new File(filePath); + File file = new File(filePath); try { return file.createNewFile(); } catch (IOException e) { @@ -54,10 +62,13 @@ private static boolean createFileIfNotExist(String filePath) { } public static class StopProfiler implements HttpHandler { + @Override public void handle(HttpExchange exchange) throws IOException { Logger.debug("hit /profiler/stop"); - final String fileName = exchange.getRequestHeaders().getFirst(ExtensionMain.HEADER_NAME); + final String fileName = exchange + .getRequestHeaders() + .getFirst(ExtensionMain.HEADER_NAME); stopProfiler(fileName); String response = "ok"; exchange.sendResponseHeaders(200, response.length()); @@ -68,6 +79,7 @@ public void handle(HttpExchange exchange) throws IOException { } public static class StartProfiler implements HttpHandler { + @Override public void handle(HttpExchange exchange) throws IOException { Logger.debug("hit /profiler/start"); @@ -80,13 +92,19 @@ public void handle(HttpExchange exchange) throws IOException { } } - public static void stopProfiler(String fileNameSuffix) { try { - final String fileName = String.format("/tmp/profiling-data-%s.html", fileNameSuffix); - Logger.debug("stopping the profiler with filename = " + fileName + " with command = " + PROFILER_STOP_COMMAND); - AsyncProfiler.getInstance().execute(String.format(PROFILER_STOP_COMMAND, fileName)); - } catch(Exception e) { + final String fileName = String.format( + Constants.getFilePathFromEnv(), + fileNameSuffix + ); + Logger.debug( + "stopping the profiler with filename = " + fileName + ); + AsyncProfiler.getInstance().execute( + String.format(PROFILER_STOP_COMMAND, fileName) + ); + } catch (Exception e) { Logger.error("could not stop the profiler"); e.printStackTrace(); } @@ -94,7 +112,9 @@ public static void stopProfiler(String fileNameSuffix) { public static void startProfiler() { try { - Logger.debug("staring the profiler with command = " + PROFILER_START_COMMAND); + Logger.debug( + "starting the profiler with command = " + PROFILER_START_COMMAND + ); AsyncProfiler.getInstance().execute(PROFILER_START_COMMAND); } catch (IOException e) { throw new RuntimeException(e); @@ -102,9 +122,10 @@ public static void startProfiler() { } public static void registerShutdownHook() { - Logger.debug("registering shutdown hook"); - Thread shutdownHook = new Thread(new ShutdownHook(PROFILER_STOP_COMMAND)); + Logger.debug("registering shutdown hook wit command = " + PROFILER_STOP_COMMAND); + Thread shutdownHook = new Thread( + new ShutdownHook(PROFILER_STOP_COMMAND) + ); Runtime.getRuntime().addShutdownHook(shutdownHook); } - -} \ No newline at end of file +} diff --git a/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/S3Manager.java b/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/S3Manager.java index 3b55984c5..0e31a2421 100644 --- a/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/S3Manager.java +++ b/experimental/aws-lambda-java-profiler/extension/src/main/java/com/amazonaws/services/lambda/extension/S3Manager.java @@ -4,7 +4,6 @@ import java.io.File; import java.time.format.DateTimeFormatter; -import java.time.Instant; import java.time.LocalDate; import software.amazon.awssdk.core.sync.RequestBody; @@ -39,7 +38,7 @@ public void upload(String fileName, boolean isShutDownEvent) { .bucket(bucketName) .key(key) .build(); - File file = new File(String.format("/tmp/profiling-data-%s.html", suffix)); + File file = new File(String.format(Constants.getFilePathFromEnv(), suffix)); if (file.exists()) { Logger.debug("file size is " + file.length()); RequestBody requestBody = RequestBody.fromFile(file); diff --git a/experimental/aws-lambda-java-profiler/integration_tests/create_function.sh b/experimental/aws-lambda-java-profiler/integration_tests/create_function.sh index 114909d09..12ba1cb2b 100755 --- a/experimental/aws-lambda-java-profiler/integration_tests/create_function.sh +++ b/experimental/aws-lambda-java-profiler/integration_tests/create_function.sh @@ -2,6 +2,7 @@ # Set variables FUNCTION_NAME="aws-lambda-java-profiler-function-${GITHUB_RUN_ID}" +FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS="aws-lambda-java-profiler-function-custom-${GITHUB_RUN_ID}" ROLE_NAME="aws-lambda-java-profiler-role-${GITHUB_RUN_ID}" HANDLER="helloworld.Handler::handleRequest" RUNTIME="java21" @@ -9,6 +10,8 @@ LAYER_ARN=$(cat /tmp/layer_arn) JAVA_TOOL_OPTIONS="-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -javaagent:/opt/profiler-extension.jar" AWS_LAMBDA_PROFILER_RESULTS_BUCKET_NAME="aws-lambda-java-profiler-bucket-${GITHUB_RUN_ID}" +AWS_LAMBDA_PROFILER_START_COMMAND="start,event=wall,interval=1us,file=/tmp/profile.jfr" +AWS_LAMBDA_PROFILER_STOP_COMMAND="stop,file=%s" # Compile the Hello World project cd integration_tests/helloworld @@ -63,6 +66,19 @@ aws lambda create-function \ --environment "Variables={JAVA_TOOL_OPTIONS='$JAVA_TOOL_OPTIONS',AWS_LAMBDA_PROFILER_RESULTS_BUCKET_NAME='$AWS_LAMBDA_PROFILER_RESULTS_BUCKET_NAME',AWS_LAMBDA_PROFILER_DEBUG='true'}" \ --layers "$LAYER_ARN" + +# Create Lambda function custom profiler options +aws lambda create-function \ + --function-name "$FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS" \ + --runtime "$RUNTIME" \ + --role "$ROLE_ARN" \ + --handler "$HANDLER" \ + --timeout 30 \ + --memory-size 512 \ + --zip-file fileb://integration_tests/helloworld/build/distributions/code.zip \ + --environment "Variables={JAVA_TOOL_OPTIONS='$JAVA_TOOL_OPTIONS',AWS_LAMBDA_PROFILER_RESULTS_BUCKET_NAME='$AWS_LAMBDA_PROFILER_RESULTS_BUCKET_NAME',AWS_LAMBDA_PROFILER_DEBUG='true',AWS_LAMBDA_PROFILER_START_COMMAND='$AWS_LAMBDA_PROFILER_START_COMMAND',AWS_LAMBDA_PROFILER_STOP_COMMAND='$AWS_LAMBDA_PROFILER_STOP_COMMAND'}" \ + --layers "$LAYER_ARN" + echo "Lambda function '$FUNCTION_NAME' created successfully with Java 21 runtime" echo "Waiting the function to be ready so we can invoke it..." diff --git a/experimental/aws-lambda-java-profiler/integration_tests/helloworld/build.gradle b/experimental/aws-lambda-java-profiler/integration_tests/helloworld/build.gradle index 927317f8f..79ffa030a 100644 --- a/experimental/aws-lambda-java-profiler/integration_tests/helloworld/build.gradle +++ b/experimental/aws-lambda-java-profiler/integration_tests/helloworld/build.gradle @@ -1,11 +1,15 @@ -apply plugin: 'java' +plugins { + id 'java' +} repositories { mavenCentral() } -sourceCompatibility = 21 -targetCompatibility = 21 +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} dependencies { implementation ( @@ -24,4 +28,5 @@ task buildZip(type: Zip) { } } + build.dependsOn buildZip \ No newline at end of file diff --git a/experimental/aws-lambda-java-profiler/integration_tests/invoke_function.sh b/experimental/aws-lambda-java-profiler/integration_tests/invoke_function.sh index 741eec140..39b0dd885 100755 --- a/experimental/aws-lambda-java-profiler/integration_tests/invoke_function.sh +++ b/experimental/aws-lambda-java-profiler/integration_tests/invoke_function.sh @@ -32,8 +32,8 @@ fi echo "Function output:" cat output.json -echo "$LOG_RESULT" | base64 --decode | grep "starting the profiler for coldstart" || exit 1 -echo "$LOG_RESULT" | base64 --decode | grep -v "uploading" || exit 1 +echo "$LOG_RESULT" | base64 --decode | grep "starting the profiler for coldstart" || { echo "ERROR: Profiler did not start for coldstart"; exit 1; } +echo "$LOG_RESULT" | base64 --decode | grep -v "uploading" || { echo "ERROR: Unexpected upload detected on cold start"; exit 1; } # Clean up the output file rm output.json @@ -68,7 +68,7 @@ fi echo "Function output:" cat output.json -echo "$LOG_RESULT" | base64 --decode | grep "uploading" || exit 1 +echo "$LOG_RESULT" | base64 --decode | grep "uploading" || { echo "ERROR: Upload not detected on warm start"; exit 1; } # Clean up the output file rm output.json diff --git a/experimental/aws-lambda-java-profiler/integration_tests/invoke_function_custom_options.sh b/experimental/aws-lambda-java-profiler/integration_tests/invoke_function_custom_options.sh new file mode 100755 index 000000000..6cf927ae0 --- /dev/null +++ b/experimental/aws-lambda-java-profiler/integration_tests/invoke_function_custom_options.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +# Set variables +FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS="aws-lambda-java-profiler-function-custom-${GITHUB_RUN_ID}" +PAYLOAD='{"key": "value"}' + +# Expected profiler commands (should match create_function.sh) +EXPECTED_START_COMMAND="start,event=wall,interval=1us,file=/tmp/profile.jfr" +EXPECTED_STOP_COMMAND="stop,file=%s" + +echo "Invoking Lambda function with custom profiler options: $FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS" + +# Invoke the Lambda function synchronously and capture the response +RESPONSE=$(aws lambda invoke \ + --function-name "$FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS" \ + --payload "$PAYLOAD" \ + --cli-binary-format raw-in-base64-out \ + --log-type Tail \ + output.json) + +# Extract the status code and log result from the response +STATUS_CODE=$(echo "$RESPONSE" | jq -r '.StatusCode') +LOG_RESULT=$(echo "$RESPONSE" | jq -r '.LogResult') + +echo "Function invocation completed with status code: $STATUS_CODE" + +# Decode and display the logs +if [ -n "$LOG_RESULT" ]; then + echo "Function logs:" + echo "$LOG_RESULT" | base64 --decode +else + echo "No logs available." +fi + +# Display the function output +echo "Function output:" +cat output.json + +# Verify profiler started +echo "$LOG_RESULT" | base64 --decode | grep "starting the profiler for coldstart" || { echo "ERROR: Profiler did not start for coldstart"; exit 1; } + +# Verify custom start command is being used +echo "$LOG_RESULT" | base64 --decode | grep "$EXPECTED_START_COMMAND" || { echo "ERROR: Expected start command not found: $EXPECTED_START_COMMAND"; exit 1; } +echo "$LOG_RESULT" | base64 --decode | grep "$EXPECTED_STOP_COMMAND" || { echo "ERROR: Expected stop command not found: $EXPECTED_STOP_COMMAND"; exit 1; } + +# Verify no upload on cold start +echo "$LOG_RESULT" | base64 --decode | grep -v "uploading" || { echo "ERROR: Unexpected upload detected on cold start"; exit 1; } + +# Clean up the output file +rm output.json + + +# Invoke it a second time for warm start +echo "Invoking Lambda function (warm start): $FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS" + +# Invoke the Lambda function synchronously and capture the response +RESPONSE=$(aws lambda invoke \ + --function-name "$FUNCTION_NAME_CUSTOM_PROFILER_OPTIONS" \ + --payload "$PAYLOAD" \ + --cli-binary-format raw-in-base64-out \ + --log-type Tail \ + output.json) + +# Extract the status code and log result from the response +STATUS_CODE=$(echo "$RESPONSE" | jq -r '.StatusCode') +LOG_RESULT=$(echo "$RESPONSE" | jq -r '.LogResult') + +echo "Function invocation completed with status code: $STATUS_CODE" + +# Decode and display the logs +if [ -n "$LOG_RESULT" ]; then + echo "Function logs:" + echo "$LOG_RESULT" | base64 --decode +else + echo "No logs available." +fi + +# Display the function output +echo "Function output:" +cat output.json + +# Verify upload happens on warm start +echo "$LOG_RESULT" | base64 --decode | grep "uploading" || { echo "ERROR: Upload not detected on warm start"; exit 1; } + +# Clean up the output file +rm output.json diff --git a/samples/custom-serialization/fastJson/HelloWorldFunction/pom.xml b/samples/custom-serialization/fastJson/HelloWorldFunction/pom.xml index 7d3c44246..2a963ca21 100644 --- a/samples/custom-serialization/fastJson/HelloWorldFunction/pom.xml +++ b/samples/custom-serialization/fastJson/HelloWorldFunction/pom.xml @@ -35,7 +35,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.1 diff --git a/samples/custom-serialization/gson/HelloWorldFunction/pom.xml b/samples/custom-serialization/gson/HelloWorldFunction/pom.xml index fd4271824..47d04926a 100644 --- a/samples/custom-serialization/gson/HelloWorldFunction/pom.xml +++ b/samples/custom-serialization/gson/HelloWorldFunction/pom.xml @@ -34,7 +34,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.1 diff --git a/samples/custom-serialization/jackson-jr/HelloWorldFunction/build.gradle b/samples/custom-serialization/jackson-jr/HelloWorldFunction/build.gradle index 71c89b7ac..480abfded 100644 --- a/samples/custom-serialization/jackson-jr/HelloWorldFunction/build.gradle +++ b/samples/custom-serialization/jackson-jr/HelloWorldFunction/build.gradle @@ -14,5 +14,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.2' } -sourceCompatibility = 21 -targetCompatibility = 21 +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} diff --git a/samples/custom-serialization/moshi/HelloWorldFunction/pom.xml b/samples/custom-serialization/moshi/HelloWorldFunction/pom.xml index 2773ef1f1..60277f10b 100644 --- a/samples/custom-serialization/moshi/HelloWorldFunction/pom.xml +++ b/samples/custom-serialization/moshi/HelloWorldFunction/pom.xml @@ -35,7 +35,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.1 diff --git a/samples/custom-serialization/request-stream-handler/HelloWorldFunction/pom.xml b/samples/custom-serialization/request-stream-handler/HelloWorldFunction/pom.xml index f6ca21bf7..15e16439d 100644 --- a/samples/custom-serialization/request-stream-handler/HelloWorldFunction/pom.xml +++ b/samples/custom-serialization/request-stream-handler/HelloWorldFunction/pom.xml @@ -34,7 +34,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.1 diff --git a/samples/kinesis-firehose-event-handler/pom.xml b/samples/kinesis-firehose-event-handler/pom.xml index 56c959244..0db8ed83a 100644 --- a/samples/kinesis-firehose-event-handler/pom.xml +++ b/samples/kinesis-firehose-event-handler/pom.xml @@ -35,6 +35,9 @@ 1.8 1.8 UTF-8 + 5.12.2 + 3.5.4 + @@ -52,7 +55,7 @@ org.junit.jupiter junit-jupiter - RELEASE + ${junit-jupiter.version} test @@ -68,7 +71,10 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + ${maven-surefire-plugin.version} + + true +