diff --git a/.github/actions/rust-build/action.yml b/.github/actions/rust-build/action.yml index 85b5c0e85..6c393d1cb 100644 --- a/.github/actions/rust-build/action.yml +++ b/.github/actions/rust-build/action.yml @@ -7,6 +7,10 @@ inputs: toolchain: required: true description: "the Rust toolchain to use" + run-tests: + required: true + default: true + description: "whether to run tests in addition to building" runs: using: "composite" @@ -22,5 +26,6 @@ runs: run: cargo build --all-features --verbose --package ${{ inputs.package }} - name: Run tests + if: ${{ inputs.run-tests == 'true' }} shell: bash run: cargo test --all-features --verbose --package ${{ inputs.package }} diff --git a/.github/workflows/build-events.yml b/.github/workflows/build-events.yml index 6894e3316..624f96d64 100644 --- a/.github/workflows/build-events.yml +++ b/.github/workflows/build-events.yml @@ -16,13 +16,12 @@ jobs: strategy: matrix: toolchain: - - "1.71.1" # Current MSRV + - "1.82.0" # Current MSRV - stable env: RUST_BACKTRACE: 1 steps: - uses: actions/checkout@v3 - - name: Build events uses: ./.github/actions/rust-build with: @@ -34,6 +33,23 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - - name: Test individual event features run: make check-event-features + semver: + name: semver + needs: [build, check-event-features] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check `aws_lambda_events` semver with only default features + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: stable + package: aws_lambda_events + feature-group: default-features + - name: Check `aws_lambda_events` semver with all features + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: stable + package: aws_lambda_events + feature-group: all-features diff --git a/.github/workflows/build-extension.yml b/.github/workflows/build-extension.yml index cb23c2890..f823dbb40 100644 --- a/.github/workflows/build-extension.yml +++ b/.github/workflows/build-extension.yml @@ -1,4 +1,4 @@ -name: Check Lambda Runtime +name: Check Lambda Extension on: push: @@ -22,22 +22,37 @@ jobs: strategy: matrix: toolchain: - - "1.71.1" # Current MSRV + - "1.82.0" # Current MSRV - stable env: RUST_BACKTRACE: 1 steps: - uses: actions/checkout@v3 - - name: Build Runtime API Client uses: ./.github/actions/rust-build with: package: lambda_runtime_api_client toolchain: ${{ matrix.toolchain}} - - - name: Build Extensions runtime uses: ./.github/actions/rust-build with: package: lambda-extension toolchain: ${{ matrix.toolchain}} + semver: + name: semver + needs: build-runtime + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check `lambda-extension` semver with only default features + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: stable + package: lambda-extension + feature-group: default-features + - name: Check `lambda-extension` semver with all features + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: stable + package: lambda-extension + feature-group: all-features diff --git a/.github/workflows/build-integration-test.yml b/.github/workflows/build-integration-test.yml new file mode 100644 index 000000000..dd9bd68fa --- /dev/null +++ b/.github/workflows/build-integration-test.yml @@ -0,0 +1,40 @@ +name: Build integration tests + +on: + push: + paths: + - 'lambda-runtime-api-client/**' + - 'lambda-runtime/**' + - 'lambda-http/**' + - 'lambda-extension/**' + - 'Cargo.toml' + + pull_request: + paths: + - 'lambda-runtime-api-client/**' + - 'lambda-runtime/**' + - 'lambda-http/**' + - 'lambda-extension/**' + - 'Cargo.toml' + +jobs: + build-runtime: + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - "1.82.0" # Current MSRV + - stable + env: + RUST_BACKTRACE: 1 + steps: + - uses: actions/checkout@v3 + + - name: Build Integration tests + uses: ./.github/actions/rust-build + with: + package: lambda-integration-tests + toolchain: ${{ matrix.toolchain}} + # the tests will generally fail in ci since they make a network call to a real endpoint, + # this step is just designed to make sure they build successfully + run-tests: false diff --git a/.github/workflows/build-runtime.yml b/.github/workflows/build-runtime.yml index dfc59ee86..0327bd34e 100644 --- a/.github/workflows/build-runtime.yml +++ b/.github/workflows/build-runtime.yml @@ -21,27 +21,42 @@ jobs: strategy: matrix: toolchain: - - "1.71.1" # Current MSRV + - "1.82.0" # Current MSRV - stable env: RUST_BACKTRACE: 1 steps: - uses: actions/checkout@v3 - - name: Build Runtime API Client uses: ./.github/actions/rust-build with: package: lambda_runtime_api_client toolchain: ${{ matrix.toolchain}} - - name: Build Functions runtime uses: ./.github/actions/rust-build with: package: lambda_runtime toolchain: ${{ matrix.toolchain}} - - name: Build HTTP layer uses: ./.github/actions/rust-build with: package: lambda_http toolchain: ${{ matrix.toolchain}} + semver: + name: semver + needs: build-runtime + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check `lambda_runtime_api_client`, `lambda_runtime`, lambda_http` semver with only default features + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: stable + package: lambda_runtime_api_client, lambda_runtime, lambda_http + feature-group: default-features + - name: Check `lambda_runtime_api_client`, `lambda_runtime`, lambda_http` semver with all features + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: stable + package: lambda_runtime_api_client, lambda_runtime, lambda_http + feature-group: all-features diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml new file mode 100644 index 000000000..4e26c31c1 --- /dev/null +++ b/.github/workflows/check-docs.yml @@ -0,0 +1,39 @@ +name: Check rustdocs +# this is its own workflow since we to to use unstable +# to have the docs.rs display of feature flags + +on: + push: + paths: + - 'lambda-runtime/**' + - 'lambda-runtime-api-client/**' + - 'lambda-http/**' + - 'lambda-events/**' + - 'lambda-extension/**' + - 'Cargo.toml' + + pull_request: + paths: + - 'lambda-runtime/**' + - 'lambda-runtime-api-client/**' + - 'lambda-http/**' + - 'lambda-events/**' + - 'lambda-extension/**' + - 'Cargo.toml' + +jobs: + build-runtime: + runs-on: ubuntu-latest + + env: + RUST_BACKTRACE: 1 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + + - name: Check documentation + shell: bash + env: + RUSTFLAGS: --cfg docsrs + RUSTDOCFLAGS: --cfg docsrs -Dwarnings + run: cargo doc --no-deps --document-private-items --all-features diff --git a/.github/workflows/run-integration-test.yml b/.github/workflows/run-integration-test.yml index f1e30d097..a4fd604b6 100644 --- a/.github/workflows/run-integration-test.yml +++ b/.github/workflows/run-integration-test.yml @@ -19,9 +19,9 @@ jobs: platform: linux arch: x86_64 - name: install Zig toolchain - uses: korandoru/setup-zig@v1 + uses: mlugg/setup-zig@v2 with: - zig-version: 0.10.0 + version: 0.10.0 - name: install SAM uses: aws-actions/setup-sam@v2 with: diff --git a/.github/workflows/test-rie.yml b/.github/workflows/test-rie.yml new file mode 100644 index 000000000..5d777e2dc --- /dev/null +++ b/.github/workflows/test-rie.yml @@ -0,0 +1,50 @@ +name: Test with RIE + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + test-rie: + runs-on: ubuntu-latest + strategy: + matrix: + example: [basic-lambda, basic-sqs] + + steps: + - uses: actions/checkout@v4 + + - name: Build and test ${{ matrix.example }} with RIE + run: | + docker build -f Dockerfile.rie --build-arg EXAMPLE=${{ matrix.example }} -t rust-lambda-rie-test-${{ matrix.example }} . + + # Start container in background + docker run -d -p 9000:8080 --name rie-test-${{ matrix.example }} rust-lambda-rie-test-${{ matrix.example }} + + # Wait for container to be ready + sleep 5 + + # Test the function based on example type + if [ "${{ matrix.example }}" = "basic-lambda" ]; then + PAYLOAD='{"command": "test from CI"}' + elif [ "${{ matrix.example }}" = "basic-sqs" ]; then + PAYLOAD='{"Records": [{"body": "{\"id\": \"123\", \"text\": \"hello from SQS\"}", "messageId": "test-id", "receiptHandle": "test-handle", "attributes": {}, "messageAttributes": {}, "md5OfBody": "test-md5", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:test-queue", "awsRegion": "us-east-1"}]}' + fi + + # Make request and verify response + RESPONSE=$(curl -s -XPOST 'http://localhost:9000/2015-03-31/functions/function/invocations' \ + -d "$PAYLOAD" \ + -H 'Content-Type: application/json') + + echo "Response: $RESPONSE" + + # Basic validation that we got a response (not empty) + if [ -z "$RESPONSE" ]; then + echo "Error: Empty response" + exit 1 + fi + + # Stop container + docker stop rie-test-${{ matrix.example }} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 867e9c0db..7baf97c2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,5 @@ pin-project-lite = "0.2" tower = "0.5" tower-layer = "0.3" tower-service = "0.3" + + diff --git a/Dockerfile.rie b/Dockerfile.rie new file mode 100644 index 000000000..1a46b5771 --- /dev/null +++ b/Dockerfile.rie @@ -0,0 +1,25 @@ +FROM public.ecr.aws/lambda/provided:al2023 + +RUN dnf install -y gcc +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/local/bin/aws-lambda-rie +RUN chmod +x /usr/local/bin/aws-lambda-rie + +ARG EXAMPLE=basic-lambda + +COPY Cargo.* /build/ +COPY lambda-runtime /build/lambda-runtime +COPY lambda-runtime-api-client /build/lambda-runtime-api-client +COPY lambda-events /build/lambda-events +COPY lambda-http /build/lambda-http +COPY lambda-extension /build/lambda-extension +COPY examples/${EXAMPLE} /build/examples/${EXAMPLE} + +WORKDIR /build/examples/${EXAMPLE} +RUN cargo build --release +RUN cp target/release/${EXAMPLE} ${LAMBDA_RUNTIME_DIR}/bootstrap + +ENTRYPOINT [] +CMD [ "/usr/local/bin/aws-lambda-rie", "/var/runtime/bootstrap" ] \ No newline at end of file diff --git a/Makefile b/Makefile index ecfd76231..155b7ea16 100644 --- a/Makefile +++ b/Makefile @@ -108,4 +108,7 @@ check-event-features: cargo test --package aws_lambda_events --no-default-features --features streams fmt: - cargo +nightly fmt --all \ No newline at end of file + cargo +nightly fmt --all + +test-rie: + ./scripts/test-rie.sh $(EXAMPLE) \ No newline at end of file diff --git a/README.md b/README.md index 1a93d85c6..954b195c1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Rust Runtime for AWS Lambda -[![Build Status](https://github.com/awslabs/aws-lambda-rust-runtime/workflows/Rust/badge.svg)](https://github.com/awslabs/aws-lambda-rust-runtime/actions) +[![Build Status](https://github.com/awslabs/aws-lambda-rust-runtime/actions/workflows/check-examples.yml/badge.svg)](https://github.com/awslabs/aws-lambda-rust-runtime/actions) This package makes it easy to run AWS Lambda Functions written in Rust. This workspace includes multiple crates: @@ -37,6 +37,11 @@ Or PiP on any system with Python 3 installed: ```bash pip3 install cargo-lambda ``` +Alternative, install the pip package as an executable using [uv](https://docs.astral.sh/uv/) + +```bash +uv tool install cargo-lambda +``` See other installation options in [the Cargo Lambda documentation](https://www.cargo-lambda.info/guide/installation.html). @@ -82,7 +87,7 @@ when a function invocation fails, AWS Lambda expects you to return an object tha } ``` -The Rust Runtime for Lambda uses a struct called `Diagnostic` to represent function errors internally. The runtime implements the converstion of several general errors types, like `std::error::Error`, into `Diagnostic`. For these general implementations, the `error_type` is the name of the value type returned by your function. For example, if your function returns `lambda_runtime::Error`, the `error_type` will be something like `alloc::boxed::Box`, which is not very descriptive. +The Rust Runtime for Lambda uses a struct called `Diagnostic` to represent function errors internally. The runtime implements the conversion of several general error types, like `std::error::Error`, into `Diagnostic`. For these general implementations, the `error_type` is the name of the value type returned by your function. For example, if your function returns `lambda_runtime::Error`, the `error_type` will be something like `alloc::boxed::Box`, which is not very descriptive. ### Implement your own Diagnostic @@ -126,6 +131,52 @@ async fn handler(_event: LambdaEvent) -> Result<(), Diagnostic> { You can see more examples on how to use these error crates in our [example repository](https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples/basic-error-error-crates-integration). +### Graceful shutdown + +`lambda_runtime` offers a helper to simplify configuring graceful shutdown signal handling, `spawn_graceful_shutdown_handler()`. This requires the `graceful-shutdown` feature flag and only supports Unix systems. + +You can use it by passing a `FnOnce` closure that returns an async block. That async block will be executed +when the function receives a `SIGTERM` or `SIGKILL`. + +Note that this helper is opinionated in a number of ways. Most notably: +1. It spawns a task to drive your signal handlers +2. It registers a 'no-op' extension in order to enable graceful shutdown signals +3. It panics on unrecoverable errors + +If you prefer to fine-tune the behavior, refer to the implementation of `spawn_graceful_shutdown_handler()` as a starting point for your own. + +For more information on graceful shutdown handling in AWS Lambda, see: [aws-samples/graceful-shutdown-with-aws-lambda](https://github.com/aws-samples/graceful-shutdown-with-aws-lambda). + +Complete example (cleaning up a non-blocking tracing writer): + +```rust,no_run +use lambda_runtime::{service_fn, LambdaEvent, Error}; +use serde_json::{json, Value}; + +#[tokio::main] +async fn main() -> Result<(), Error> { + let func = service_fn(func); + + let (writer, log_guard) = tracing_appender::non_blocking(std::io::stdout()); + lambda_runtime::tracing::init_default_subscriber_with_writer(writer); + + let shutdown_hook = || async move { + std::mem::drop(log_guard); + }; + lambda_runtime::spawn_graceful_shutdown_handler(shutdown_hook).await; + + lambda_runtime::run(func).await?; + Ok(()) +} + +async fn func(event: LambdaEvent) -> Result { + let (event, _context) = event.into_parts(); + let first_name = event["firstName"].as_str().unwrap_or("world"); + + Ok(json!({ "message": format!("Hello, {}!", first_name) })) +} +``` + ## Building and deploying your Lambda functions If you already have Cargo Lambda installed in your machine, run the next command to build your function: @@ -341,6 +392,31 @@ curl -v -X POST \ You can read more about how [cargo lambda watch](https://www.cargo-lambda.info/commands/watch.html) and [cargo lambda invoke](https://www.cargo-lambda.info/commands/invoke.html) work on the project's [documentation page](https://www.cargo-lambda.info). +### Local testing with Runtime Interface Emulator (RIE) + +For testing with the official AWS Lambda Runtime Interface Emulator, use the provided RIE testing infrastructure: + +```bash +make test-rie +``` + +By default, this uses the `basic-lambda` example. To test a different example: + +```bash +make test-rie EXAMPLE=basic-sqs +make test-rie EXAMPLE=http-basic-lambda +``` + +This command will: +1. Build a Docker image with Rust toolchain and RIE +2. Compile the specified example inside the Linux container +3. Start the RIE container on port 9000 +4. Display the appropriate curl command for testing + +Different examples expect different payload formats. Check the example's source code in `examples/EXAMPLE_NAME/src/main.rs` + +This provides automated testing with Docker and RIE, ensuring your Lambda functions work in a Linux environment identical to AWS Lambda. + ### Lambda Debug Proxy Lambdas can be run and debugged locally using a special [Lambda debug proxy](https://github.com/rimutaka/lambda-debug-proxy) (a non-AWS repo maintained by @rimutaka), which is a Lambda function that forwards incoming requests to one AWS SQS queue and reads responses from another queue. A local proxy running on your development computer reads the queue, calls your Lambda locally and sends back the response. This approach allows debugging of Lambda functions locally while being part of your AWS workflow. The Lambda handler code does not need to be modified between the local and AWS versions. @@ -409,7 +485,7 @@ fn main() -> Result<(), Box> { ## Supported Rust Versions (MSRV) -The AWS Lambda Rust Runtime requires a minimum of Rust 1.71.1, and is not guaranteed to build on compiler versions earlier than that. +The AWS Lambda Rust Runtime requires a minimum of Rust 1.82.0, and is not guaranteed to build on compiler versions earlier than that. ## Security diff --git a/examples/advanced-appconfig-feature-flags/Cargo.toml b/examples/advanced-appconfig-feature-flags/Cargo.toml index 52ebb843f..51b708ecc 100644 --- a/examples/advanced-appconfig-feature-flags/Cargo.toml +++ b/examples/advanced-appconfig-feature-flags/Cargo.toml @@ -15,10 +15,10 @@ edition = "2021" # and it will keep the alphabetic ordering for you. [dependencies] -async-trait = "0.1.68" -lambda_runtime = "0.13" -reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } +async-trait = "0.1.88" +lambda_runtime = { path = "../../lambda-runtime" } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -thiserror = "1.0" +thiserror = "2.0" tokio = { version = "1", features = ["macros"] } diff --git a/examples/advanced-appconfig-feature-flags/cdk/package-lock.json b/examples/advanced-appconfig-feature-flags/cdk/package-lock.json index 61c9a537b..8f30ffeff 100644 --- a/examples/advanced-appconfig-feature-flags/cdk/package-lock.json +++ b/examples/advanced-appconfig-feature-flags/cdk/package-lock.json @@ -8,7 +8,7 @@ "name": "cdk", "version": "0.1.0", "dependencies": { - "aws-cdk-lib": "2.159.1", + "aws-cdk-lib": "2.193.0", "cargo-lambda-cdk": "^0.0.22", "constructs": "^10.0.0", "source-map-support": "^0.5.21" @@ -40,14 +40,10 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.203", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.203.tgz", - "integrity": "sha512-7ZhjD0L62dhWL0yzoLCxvJTU3tLHTz/yEg6GKt3foSj+ljVR1KSP8MuAi+QPb4pT7ZTfVzELMtI36Y/ROuL3ig==" - }, - "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" + "version": "2.2.233", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.233.tgz", + "integrity": "sha512-OH5ZN1F/0wwOUwzVUSvE0/syUOi44H9the6IG16anlSptfeQ1fvduJazZAKRuJLtautPbiqxllyOrtWh6LhX8A==", + "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { "version": "2.1.0", @@ -55,19 +51,20 @@ "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==" }, "node_modules/@aws-cdk/cloud-assembly-schema": { - "version": "36.3.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.3.0.tgz", - "integrity": "sha512-mLSYgcMFTNCXrGAD7xob95p9s47/7WwEWUJiexxM46H2GxiijhlhLQJs31AS5uRRP6Cx1DLEu4qayKAUOOVGrw==", + "version": "41.2.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-41.2.0.tgz", + "integrity": "sha512-JaulVS6z9y5+u4jNmoWbHZRs9uGOnmn/ktXygNWKNu1k6lF3ad4so3s18eRu15XCbUIomxN9WPYT6Ehh7hzONw==", "bundleDependencies": [ "jsonschema", "semver" ], + "license": "Apache-2.0", "dependencies": { - "jsonschema": "^1.4.1", - "semver": "^7.6.3" + "jsonschema": "~1.4.1", + "semver": "^7.7.1" }, "engines": { - "node": ">= 18.18.0" + "node": ">= 14.15.0" } }, "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { @@ -79,7 +76,7 @@ } }, "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.1", "inBundle": true, "license": "ISC", "bin": { @@ -1288,9 +1285,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.159.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.159.1.tgz", - "integrity": "sha512-zcOyAs3+DTu+CtLehdOgvyosZ7nbLZ+OfBE6uVNMshXm957oXJrLsu6hehLt81TDxfItWYNluFcXkwepZDm6Ng==", + "version": "2.193.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.193.0.tgz", + "integrity": "sha512-Bsf11FM85+s9jSAT8JfDNlrSz6LF3Xa2eSNOyMLcXNopI7eVXP1U6opRHK0waaZGLmQfOwsbSp/9XRMKikkazg==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -1304,21 +1301,21 @@ "yaml", "mime-types" ], + "license": "Apache-2.0", "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.202", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-awscli-v1": "^2.2.229", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^41.0.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.2.0", + "fs-extra": "^11.3.0", "ignore": "^5.3.2", - "jsonschema": "^1.4.1", + "jsonschema": "^1.5.0", "mime-types": "^2.1.35", "minimatch": "^3.1.2", "punycode": "^2.3.1", - "semver": "^7.6.3", - "table": "^6.8.2", + "semver": "^7.7.1", + "table": "^6.9.0", "yaml": "1.10.2" }, "engines": { @@ -1432,12 +1429,22 @@ "license": "MIT" }, "node_modules/aws-cdk-lib/node_modules/fast-uri": { - "version": "3.0.1", + "version": "3.0.6", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "inBundle": true, - "license": "MIT" + "license": "BSD-3-Clause" }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.2.0", + "version": "11.3.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -1487,7 +1494,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/jsonschema": { - "version": "1.4.1", + "version": "1.5.0", "inBundle": true, "license": "MIT", "engines": { @@ -1546,7 +1553,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.6.3", + "version": "7.7.1", "inBundle": true, "license": "ISC", "bin": { @@ -1597,7 +1604,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/table": { - "version": "6.8.2", + "version": "6.9.0", "inBundle": true, "license": "BSD-3-Clause", "dependencies": { @@ -1619,13 +1626,6 @@ "node": ">= 10.0.0" } }, - "node_modules/aws-cdk-lib/node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/aws-cdk-lib/node_modules/yaml": { "version": "1.10.2", "inBundle": true, @@ -3835,6 +3835,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -4379,14 +4380,9 @@ } }, "@aws-cdk/asset-awscli-v1": { - "version": "2.2.203", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.203.tgz", - "integrity": "sha512-7ZhjD0L62dhWL0yzoLCxvJTU3tLHTz/yEg6GKt3foSj+ljVR1KSP8MuAi+QPb4pT7ZTfVzELMtI36Y/ROuL3ig==" - }, - "@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" + "version": "2.2.233", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.233.tgz", + "integrity": "sha512-OH5ZN1F/0wwOUwzVUSvE0/syUOi44H9the6IG16anlSptfeQ1fvduJazZAKRuJLtautPbiqxllyOrtWh6LhX8A==" }, "@aws-cdk/asset-node-proxy-agent-v6": { "version": "2.1.0", @@ -4394,12 +4390,12 @@ "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==" }, "@aws-cdk/cloud-assembly-schema": { - "version": "36.3.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.3.0.tgz", - "integrity": "sha512-mLSYgcMFTNCXrGAD7xob95p9s47/7WwEWUJiexxM46H2GxiijhlhLQJs31AS5uRRP6Cx1DLEu4qayKAUOOVGrw==", + "version": "41.2.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-41.2.0.tgz", + "integrity": "sha512-JaulVS6z9y5+u4jNmoWbHZRs9uGOnmn/ktXygNWKNu1k6lF3ad4so3s18eRu15XCbUIomxN9WPYT6Ehh7hzONw==", "requires": { - "jsonschema": "^1.4.1", - "semver": "^7.6.3" + "jsonschema": "~1.4.1", + "semver": "^7.7.1" }, "dependencies": { "jsonschema": { @@ -4407,7 +4403,7 @@ "bundled": true }, "semver": { - "version": "7.6.3", + "version": "7.7.1", "bundled": true } } @@ -5358,24 +5354,23 @@ } }, "aws-cdk-lib": { - "version": "2.159.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.159.1.tgz", - "integrity": "sha512-zcOyAs3+DTu+CtLehdOgvyosZ7nbLZ+OfBE6uVNMshXm957oXJrLsu6hehLt81TDxfItWYNluFcXkwepZDm6Ng==", + "version": "2.193.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.193.0.tgz", + "integrity": "sha512-Bsf11FM85+s9jSAT8JfDNlrSz6LF3Xa2eSNOyMLcXNopI7eVXP1U6opRHK0waaZGLmQfOwsbSp/9XRMKikkazg==", "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.202", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-awscli-v1": "^2.2.229", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^41.0.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.2.0", + "fs-extra": "^11.3.0", "ignore": "^5.3.2", - "jsonschema": "^1.4.1", + "jsonschema": "^1.5.0", "mime-types": "^2.1.35", "minimatch": "^3.1.2", "punycode": "^2.3.1", - "semver": "^7.6.3", - "table": "^6.8.2", + "semver": "^7.7.1", + "table": "^6.9.0", "yaml": "1.10.2" }, "dependencies": { @@ -5448,11 +5443,11 @@ "bundled": true }, "fast-uri": { - "version": "3.0.1", + "version": "3.0.6", "bundled": true }, "fs-extra": { - "version": "11.2.0", + "version": "11.3.0", "bundled": true, "requires": { "graceful-fs": "^4.2.0", @@ -5485,7 +5480,7 @@ } }, "jsonschema": { - "version": "1.4.1", + "version": "1.5.0", "bundled": true }, "lodash.truncate": { @@ -5519,7 +5514,7 @@ "bundled": true }, "semver": { - "version": "7.6.3", + "version": "7.7.1", "bundled": true }, "slice-ansi": { @@ -5548,7 +5543,7 @@ } }, "table": { - "version": "6.8.2", + "version": "6.9.0", "bundled": true, "requires": { "ajv": "^8.0.1", @@ -5562,12 +5557,6 @@ "version": "2.0.1", "bundled": true }, - "uri-js": { - "version": "4.4.1", - "requires": { - "punycode": "^2.1.0" - } - }, "yaml": { "version": "1.10.2", "bundled": true @@ -7200,7 +7189,8 @@ "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true }, "shebang-command": { "version": "2.0.0", diff --git a/examples/advanced-appconfig-feature-flags/cdk/package.json b/examples/advanced-appconfig-feature-flags/cdk/package.json index 83cb9552f..688a31a07 100644 --- a/examples/advanced-appconfig-feature-flags/cdk/package.json +++ b/examples/advanced-appconfig-feature-flags/cdk/package.json @@ -20,7 +20,7 @@ "typescript": "~5.4.5" }, "dependencies": { - "aws-cdk-lib": "2.159.1", + "aws-cdk-lib": "2.193.0", "cargo-lambda-cdk": "^0.0.22", "constructs": "^10.0.0", "source-map-support": "^0.5.21" diff --git a/examples/advanced-appconfig-feature-flags/src/main.rs b/examples/advanced-appconfig-feature-flags/src/main.rs index b7d5e5152..87ec54fac 100644 --- a/examples/advanced-appconfig-feature-flags/src/main.rs +++ b/examples/advanced-appconfig-feature-flags/src/main.rs @@ -35,9 +35,9 @@ async fn function_handler( // Use the feature flag let msg = if config.spanish_response { - format!("{}, in spanish.", quote) + format!("{quote}, in spanish.") } else { - format!("{}.", quote) + format!("{quote}.") }; // Return `Response` (it will be serialized to JSON automatically by the runtime) diff --git a/examples/advanced-sqs-multiple-functions-shared-data/consumer/Cargo.toml b/examples/advanced-sqs-multiple-functions-shared-data/consumer/Cargo.toml index 69ec04a09..e82dc1d34 100644 --- a/examples/advanced-sqs-multiple-functions-shared-data/consumer/Cargo.toml +++ b/examples/advanced-sqs-multiple-functions-shared-data/consumer/Cargo.toml @@ -5,9 +5,7 @@ edition = "2021" [dependencies] #aws dependencies -aws-sdk-config = "0.35.0" -aws-sdk-sqs = "0.35.0" -aws_lambda_events = { version = "0.11.1", features = ["sqs"], default-features = false } +aws_lambda_events = { path = "../../../lambda-events", features = ["sqs"], default-features = false } #lambda runtime lambda_runtime = { path = "../../../lambda-runtime" } diff --git a/examples/advanced-sqs-multiple-functions-shared-data/pizza_lib/Cargo.toml b/examples/advanced-sqs-multiple-functions-shared-data/pizza_lib/Cargo.toml index 76631bbd8..2dd69db13 100644 --- a/examples/advanced-sqs-multiple-functions-shared-data/pizza_lib/Cargo.toml +++ b/examples/advanced-sqs-multiple-functions-shared-data/pizza_lib/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -serde = { version = "1.0.191", features = ["derive"] } +serde = { version = "1.0.219", features = ["derive"] } diff --git a/examples/advanced-sqs-multiple-functions-shared-data/producer/Cargo.toml b/examples/advanced-sqs-multiple-functions-shared-data/producer/Cargo.toml index 83aa48ab8..2772f6500 100644 --- a/examples/advanced-sqs-multiple-functions-shared-data/producer/Cargo.toml +++ b/examples/advanced-sqs-multiple-functions-shared-data/producer/Cargo.toml @@ -8,14 +8,13 @@ env = { "QUEUE_URL" = "https://changeMe" } [dependencies] #aws dependencies -aws-config = "0.57.1" -aws-sdk-config = "0.35.0" -aws-sdk-sqs = "0.35.0" +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +aws-sdk-sqs = "1.74.0" #lambda runtime lambda_runtime = { path = "../../../lambda-runtime" } -serde_json = "1.0.108" +serde_json = "1.0.140" tokio = { version = "1", features = ["macros"] } #shared lib -pizza_lib = { path = "../pizza_lib" } \ No newline at end of file +pizza_lib = { path = "../pizza_lib" } diff --git a/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs b/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs index 2a70dce31..6a2883f32 100644 --- a/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs +++ b/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs @@ -20,7 +20,7 @@ async fn main() -> Result<(), Error> { // read the queue url from the environment let queue_url = std::env::var("QUEUE_URL").expect("could not read QUEUE_URL"); // build the config from environment variables (fed by AWS Lambda) - let config = aws_config::from_env().load().await; + let config = aws_config::load_from_env().await; // create our SQS Manager let sqs_manager = SQSManager::new(aws_sdk_sqs::Client::new(&config), queue_url); let sqs_manager_ref = &sqs_manager; diff --git a/examples/advanced-sqs-partial-batch-failures/Cargo.toml b/examples/advanced-sqs-partial-batch-failures/Cargo.toml index 95050b9ad..f02e4efb3 100644 --- a/examples/advanced-sqs-partial-batch-failures/Cargo.toml +++ b/examples/advanced-sqs-partial-batch-failures/Cargo.toml @@ -5,8 +5,6 @@ edition = "2021" [dependencies] serde = "^1" -serde_derive = "^1" -serde_with = { version = "^2", features = ["json"], optional = true } serde_json = "^1" aws_lambda_events = { path = "../../lambda-events" } lambda_runtime = { path = "../../lambda-runtime" } diff --git a/examples/advanced-sqs-partial-batch-failures/src/main.rs b/examples/advanced-sqs-partial-batch-failures/src/main.rs index 42bb22533..4af67b8ce 100644 --- a/examples/advanced-sqs-partial-batch-failures/src/main.rs +++ b/examples/advanced-sqs-partial-batch-failures/src/main.rs @@ -44,7 +44,7 @@ async fn main() -> Result<(), Error> { /// Important note: your lambda sqs trigger *needs* to be configured with partial batch response support /// with the ` ReportBatchItemFailures` flag set to true, otherwise failed message will be dropped, /// for more details see: -/// https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting +/// /// /// /// Note that if you are looking for parallel processing (multithread) instead of concurrent processing, @@ -55,7 +55,7 @@ where D: DeserializeOwned, R: Future>, { - run(service_fn(|e| batch_handler(|d| f(d), e))).await + run(service_fn(|e| batch_handler(&f, e))).await } /// Helper function to lift the user provided `f` function from message to batch of messages. @@ -96,11 +96,17 @@ where } }, ) - .map(|id| BatchItemFailure { item_identifier: id }) + .map(|id| { + let mut failure_item = BatchItemFailure::default(); + failure_item.item_identifier = id; + failure_item + }) .collect(); - Ok(SqsBatchResponse { - batch_item_failures: failure_items, + Ok({ + let mut response = SqsBatchResponse::default(); + response.batch_item_failures = failure_items; + response }) } @@ -123,7 +129,7 @@ mod test { } #[tokio::test] - async fn test() -> () { + async fn test() { let msg_to_fail: SqsMessageObj = serde_json::from_str( r#"{ "messageId": "1", @@ -140,8 +146,10 @@ mod test { .unwrap(); let lambda_event = LambdaEvent { - payload: SqsEventObj { - records: vec![msg_to_fail, msg_to_succeed], + payload: { + let mut event_object = SqsEventObj::default(); + event_object.records = vec![msg_to_fail, msg_to_succeed]; + event_object }, context: Context::default(), }; diff --git a/examples/basic-cognito-post-confirmation/Cargo.toml b/examples/basic-cognito-post-confirmation/Cargo.toml index 7d2e7ab45..93369e515 100644 --- a/examples/basic-cognito-post-confirmation/Cargo.toml +++ b/examples/basic-cognito-post-confirmation/Cargo.toml @@ -15,8 +15,8 @@ edition = "2021" # and it will keep the alphabetic ordering for you. [dependencies] -aws-config = "1.5.0" -aws-sdk-ses = "1.28.0" +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +aws-sdk-ses = "1.77.0" aws_lambda_events = { path = "../../lambda-events", default-features = false, features = ["cognito"] } lambda_runtime = { path = "../../lambda-runtime" } diff --git a/examples/basic-error-error-crates-integration/Cargo.toml b/examples/basic-error-error-crates-integration/Cargo.toml index 741ec7137..24fbc8dca 100644 --- a/examples/basic-error-error-crates-integration/Cargo.toml +++ b/examples/basic-error-error-crates-integration/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" anyhow = "1" eyre = "0.6.12" lambda_runtime = { path = "../../lambda-runtime", features = ["anyhow", "eyre", "miette"] } -miette = "7.2.0" +miette = "7.6.0" serde = "1" tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-error-error-crates-integration/src/main.rs b/examples/basic-error-error-crates-integration/src/main.rs index f40485849..176bd54b3 100644 --- a/examples/basic-error-error-crates-integration/src/main.rs +++ b/examples/basic-error-error-crates-integration/src/main.rs @@ -28,7 +28,7 @@ fn miette_error() -> miette::Result<()> { /// Transform an anyhow::Error, eyre::Report, or miette::Report into a lambda_runtime::Diagnostic. /// It does it by enabling the feature `anyhow`, `eyre` or `miette` in the runtime dependency. -/// Those features enable the implementation of `From for Diagnostic` +/// Those features enable the implementation of `From for Diagnostic` /// for `anyhow::Error`, `eyre::Report`, and `miette::Report`. async fn function_handler(event: LambdaEvent) -> Result<(), Diagnostic> { match event.payload.error_type { diff --git a/examples/basic-error-handling/Cargo.toml b/examples/basic-error-handling/Cargo.toml index 1039a1392..a0267f978 100644 --- a/examples/basic-error-handling/Cargo.toml +++ b/examples/basic-error-handling/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.136" -serde_json = "1.0.81" -simple-error = "0.2.3" +serde = "1.0.219" +serde_json = "1.0.140" +simple-error = "0.3.1" tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-error-handling/src/main.rs b/examples/basic-error-handling/src/main.rs index 3bc769364..85e97428b 100644 --- a/examples/basic-error-handling/src/main.rs +++ b/examples/basic-error-handling/src/main.rs @@ -1,4 +1,4 @@ -/// See https://github.com/awslabs/aws-lambda-rust-runtime for more info on Rust runtime for AWS Lambda +/// See for more info on Rust runtime for AWS Lambda use lambda_runtime::{service_fn, tracing, Error, LambdaEvent}; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -43,7 +43,7 @@ impl std::fmt::Display for CustomError { /// Display the error struct as a JSON string fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let err_as_json = json!(self).to_string(); - write!(f, "{}", err_as_json) + write!(f, "{err_as_json}") } } @@ -66,7 +66,7 @@ pub(crate) async fn func(event: LambdaEvent) -> Result match event.event_type { EventType::SimpleError => { // generate a simple text message error using `simple_error` crate - return Err(Box::new(simple_error::SimpleError::new("A simple error as requested!"))); + Err(Box::new(simple_error::SimpleError::new("A simple error as requested!"))) } EventType::CustomError => { // generate a custom error using our own structure @@ -75,7 +75,7 @@ pub(crate) async fn func(event: LambdaEvent) -> Result req_id: ctx.request_id, msg: "A custom error as requested!".into(), }; - return Err(Box::new(cust_err)); + Err(Box::new(cust_err)) } EventType::ExternalError => { // try to open a non-existent file to get an error and propagate it with `?` @@ -94,7 +94,7 @@ pub(crate) async fn func(event: LambdaEvent) -> Result msg: "OK".into(), }; - return Ok(resp); + Ok(resp) } } } diff --git a/examples/basic-error-thiserror/Cargo.toml b/examples/basic-error-thiserror/Cargo.toml index d7c7d7251..f2b0b4499 100644 --- a/examples/basic-error-thiserror/Cargo.toml +++ b/examples/basic-error-thiserror/Cargo.toml @@ -18,5 +18,5 @@ edition = "2021" lambda_runtime = { path = "../../lambda-runtime" } serde = "1" -thiserror = "1.0.61" +thiserror = "2.0" tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-error-thiserror/src/main.rs b/examples/basic-error-thiserror/src/main.rs index 403309bf4..2c01b833d 100644 --- a/examples/basic-error-thiserror/src/main.rs +++ b/examples/basic-error-thiserror/src/main.rs @@ -1,6 +1,5 @@ use lambda_runtime::{service_fn, Diagnostic, Error, LambdaEvent}; use serde::Deserialize; -use thiserror; #[derive(Deserialize)] struct Request {} @@ -21,7 +20,7 @@ impl From for Diagnostic { }; Diagnostic { error_type: error_type.into(), - error_message: error_message.into(), + error_message, } } } diff --git a/examples/basic-lambda-external-runtime/Cargo.toml b/examples/basic-lambda-external-runtime/Cargo.toml index d6d023d8a..40f24d81b 100644 --- a/examples/basic-lambda-external-runtime/Cargo.toml +++ b/examples/basic-lambda-external-runtime/Cargo.toml @@ -4,11 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -async-channel = "1.8.0" -futures-lite = "1.13.0" +async-channel = "2.5.0" +futures-lite = "2.6.0" lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.163" -tokio = "1.28.2" - -[dev-dependencies] -tokio-test = "0.4.2" +serde = "1.0.219" +tokio = "1.46.1" diff --git a/examples/basic-lambda-external-runtime/src/main.rs b/examples/basic-lambda-external-runtime/src/main.rs index bd3b4e6c6..87891ebc4 100644 --- a/examples/basic-lambda-external-runtime/src/main.rs +++ b/examples/basic-lambda-external-runtime/src/main.rs @@ -53,7 +53,7 @@ fn main() -> Result<(), io::Error> { my_runtime(move || future::block_on(app_runtime_task(lambda_rx.clone(), shutdown_tx.clone()))); // Block the main thread until a shutdown signal is received. - future::block_on(shutdown_rx.recv()).map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err))) + future::block_on(shutdown_rx.recv()).map_err(|err| io::Error::other(format!("{err:?}"))) } pub(crate) async fn my_handler(event: LambdaEvent) -> Result { @@ -63,7 +63,7 @@ pub(crate) async fn my_handler(event: LambdaEvent) -> Result) -> Result, _size: u32) -> Vec { mod tests { use super::*; use async_trait::async_trait; - use aws_lambda_events::s3::object_lambda::Configuration; - use aws_lambda_events::s3::object_lambda::HeadObjectContext; - use aws_lambda_events::s3::object_lambda::ListObjectsContext; - use aws_lambda_events::s3::object_lambda::ListObjectsV2Context; - use aws_lambda_events::s3::object_lambda::UserIdentity; - use aws_lambda_events::s3::object_lambda::UserRequest; - use aws_lambda_events::serde_json::json; + use aws_lambda_events::s3::object_lambda::{ + Configuration, HeadObjectContext, ListObjectsContext, ListObjectsV2Context, UserIdentity, UserRequest, + }; use lambda_runtime::{Context, LambdaEvent}; use mockall::mock; - use s3::GetFile; - use s3::SendFile; + use s3::{GetFile, SendFile}; + use serde_json::json; #[tokio::test] async fn response_is_good() { mock! { FakeS3Client {} - #[async_trait] impl GetFile for FakeS3Client { - pub fn get_file(&self, url: String) -> Result, Box>; + fn get_file(&self, url: String) -> Result, Box>; } #[async_trait] impl SendFile for FakeS3Client { - pub async fn send_file(&self, route: String, token: String, vec: Vec) -> Result>; + async fn send_file(&self, route: String, token: String, vec: Vec) -> Result>; } } @@ -118,9 +113,7 @@ mod tests { .returning(|_1| Ok("IMAGE".into())); mock.expect_send_file() - .withf(|r, t, by| { - return r.eq("O_ROUTE") && t.eq("O_TOKEN") && by == "THUMBNAIL".as_bytes(); - }) + .withf(|r, t, by| r.eq("O_ROUTE") && t.eq("O_TOKEN") && by == "THUMBNAIL".as_bytes()) .returning(|_1, _2, _3| Ok("File sent.".to_string())); let payload = get_s3_event(); @@ -133,24 +126,28 @@ mod tests { } fn get_s3_event() -> S3ObjectLambdaEvent { - return S3ObjectLambdaEvent { - x_amz_request_id: ("ID".to_string()), - head_object_context: (Some(HeadObjectContext::default())), - list_objects_context: (Some(ListObjectsContext::default())), - get_object_context: (Some(GetObjectContext { - input_s3_url: ("S3_URL".to_string()), - output_route: ("O_ROUTE".to_string()), - output_token: ("O_TOKEN".to_string()), - })), - list_objects_v2_context: (Some(ListObjectsV2Context::default())), - protocol_version: ("VERSION".to_string()), - user_identity: (UserIdentity::default()), - user_request: (UserRequest::default()), - configuration: (Configuration { - access_point_arn: ("APRN".to_string()), - supporting_access_point_arn: ("SAPRN".to_string()), - payload: (json!(null)), - }), + let mut event = S3ObjectLambdaEvent::default(); + event.x_amz_request_id = "ID".to_string(); + event.head_object_context = Some(HeadObjectContext::default()); + event.list_objects_context = Some(ListObjectsContext::default()); + event.get_object_context = Some({ + let mut context = GetObjectContext::default(); + context.input_s3_url = "S3_URL".to_string(); + context.output_route = "O_ROUTE".to_string(); + context.output_token = "O_TOKEN".to_string(); + context + }); + event.list_objects_v2_context = Some(ListObjectsV2Context::default()); + event.protocol_version = "VERSION".to_string(); + event.user_identity = UserIdentity::default(); + event.user_request = UserRequest::default(); + event.configuration = { + let mut configuration = Configuration::default(); + configuration.access_point_arn = "APRN".to_string(); + configuration.supporting_access_point_arn = "SAPRN".to_string(); + configuration.payload = json!(null); + configuration }; + event } } diff --git a/examples/basic-s3-object-lambda-thumbnail/src/s3.rs b/examples/basic-s3-object-lambda-thumbnail/src/s3.rs index daba37396..69b46ec28 100644 --- a/examples/basic-s3-object-lambda-thumbnail/src/s3.rs +++ b/examples/basic-s3-object-lambda-thumbnail/src/s3.rs @@ -1,6 +1,8 @@ use async_trait::async_trait; -use aws_sdk_s3::{operation::write_get_object_response::WriteGetObjectResponseError, Client as S3Client}; -use aws_smithy_http::{byte_stream::ByteStream, result::SdkError}; +use aws_sdk_s3::{ + error::SdkError, operation::write_get_object_response::WriteGetObjectResponseError, primitives::ByteStream, + Client as S3Client, +}; use lambda_runtime::tracing; use std::{error, io::Read}; @@ -17,12 +19,17 @@ impl GetFile for S3Client { fn get_file(&self, url: String) -> Result, Box> { tracing::info!("get file url {}", url); - let resp = ureq::get(&url).call()?; - let len: usize = resp.header("Content-Length").unwrap().parse()?; + let mut res = ureq::get(&url).call()?; + let len: usize = res + .headers() + .get("Content-Length") + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()) + .unwrap(); let mut bytes: Vec = Vec::with_capacity(len); - std::io::Read::take(resp.into_reader(), 10_000_000).read_to_end(&mut bytes)?; + std::io::Read::take(res.body_mut().as_reader(), 10_000_000).read_to_end(&mut bytes)?; tracing::info!("got {} bytes", bytes.len()); @@ -46,9 +53,8 @@ impl SendFile for S3Client { .send() .await; - if write.is_err() { - let sdk_error = write.err().unwrap(); - check_error(sdk_error); + if let Err(err) = write { + check_error(err); Err("WriteGetObjectResponse creation error".into()) } else { Ok("File sent.".to_string()) @@ -65,16 +71,16 @@ fn check_error(error: SdkError) { tracing::info!("DispatchFailure"); if err.is_io() { tracing::info!("IO error"); - }; + } if err.is_timeout() { tracing::info!("Timeout error"); - }; + } if err.is_user() { tracing::info!("User error"); - }; - if err.is_other().is_some() { + } + if err.is_other() { tracing::info!("Other error"); - }; + } } SdkError::ResponseError(_err) => tracing::info!("ResponseError"), SdkError::TimeoutError(_err) => tracing::info!("TimeoutError"), diff --git a/examples/basic-s3-thumbnail/Cargo.toml b/examples/basic-s3-thumbnail/Cargo.toml index 4b9ef3dad..fdbd79be1 100644 --- a/examples/basic-s3-thumbnail/Cargo.toml +++ b/examples/basic-s3-thumbnail/Cargo.toml @@ -17,17 +17,13 @@ edition = "2021" [dependencies] aws_lambda_events = { path = "../../lambda-events" } lambda_runtime = { path = "../../lambda-runtime" } -serde = "1" tokio = { version = "1", features = ["macros"] } -aws-config = "0.55" -aws-smithy-http = "0.55.3" -aws-sdk-s3 = "0.28" +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +aws-sdk-s3 = "1.96.0" thumbnailer = "0.5.1" -mime = "0.3.16" -async-trait = "0.1.68" -webp = "=0.2.1" +mime = "0.3.17" +async-trait = "0.1.88" [dev-dependencies] -mockall = "0.11" -tokio-test = "0.4" +mockall = "0.13.1" chrono = "0.4" diff --git a/examples/basic-s3-thumbnail/src/main.rs b/examples/basic-s3-thumbnail/src/main.rs index 3eb5bfe9e..0d647b05c 100644 --- a/examples/basic-s3-thumbnail/src/main.rs +++ b/examples/basic-s3-thumbnail/src/main.rs @@ -139,16 +139,11 @@ mod tests { use super::*; use async_trait::async_trait; //use aws_lambda_events::chrono::DateTime; - use aws_lambda_events::s3::S3Bucket; - use aws_lambda_events::s3::S3Entity; - use aws_lambda_events::s3::S3Object; - use aws_lambda_events::s3::S3RequestParameters; - use aws_lambda_events::s3::S3UserIdentity; + use aws_lambda_events::s3::{S3Bucket, S3Entity, S3Object, S3RequestParameters, S3UserIdentity}; use aws_sdk_s3::operation::get_object::GetObjectError; use lambda_runtime::{Context, LambdaEvent}; use mockall::mock; - use s3::GetFile; - use s3::PutFile; + use s3::{GetFile, PutFile}; #[tokio::test] async fn response_is_good() { @@ -163,11 +158,11 @@ mod tests { #[async_trait] impl GetFile for FakeS3Client { - pub async fn get_file(&self, bucket: &str, key: &str) -> Result, GetObjectError>; + async fn get_file(&self, bucket: &str, key: &str) -> Result, GetObjectError>; } #[async_trait] impl PutFile for FakeS3Client { - pub async fn put_file(&self, bucket: &str, key: &str, bytes: Vec) -> Result; + async fn put_file(&self, bucket: &str, key: &str, bytes: Vec) -> Result; } } @@ -178,60 +173,65 @@ mod tests { .returning(|_1, _2| Ok("IMAGE".into())); mock.expect_put_file() - .withf(|bu, ke, by| { - return bu.eq("test-bucket-thumbs") && ke.eq(key) && by.eq("THUMBNAIL".as_bytes()); - }) + .withf(|bu, ke, by| bu.eq("test-bucket-thumbs") && ke.eq(key) && by.eq("THUMBNAIL".as_bytes())) .return_const(Ok("Done".to_string())); let payload = get_s3_event("ObjectCreated", bucket, key); let event = LambdaEvent { payload, context }; - let result = function_handler(event, 10, &mock).await.unwrap(); + function_handler(event, 10, &mock).await.unwrap(); - assert_eq!((), result); + assert_eq!((), ()); } fn get_s3_event(event_name: &str, bucket_name: &str, object_key: &str) -> S3Event { - return S3Event { - records: (vec![get_s3_event_record(event_name, bucket_name, object_key)]), - }; + let mut event = S3Event::default(); + event.records = vec![get_s3_event_record(event_name, bucket_name, object_key)]; + event } fn get_s3_event_record(event_name: &str, bucket_name: &str, object_key: &str) -> S3EventRecord { - let s3_entity = S3Entity { - schema_version: (Some(String::default())), - configuration_id: (Some(String::default())), - bucket: (S3Bucket { - name: (Some(bucket_name.to_string())), - owner_identity: Some(S3UserIdentity { - principal_id: (Some(String::default())), - }), - arn: (Some(String::default())), - }), - object: (S3Object { - key: (Some(object_key.to_string())), - size: (Some(1)), - url_decoded_key: (Some(String::default())), - version_id: (Some(String::default())), - e_tag: (Some(String::default())), - sequencer: (Some(String::default())), - }), + let mut s3_bucket = S3Bucket::default(); + s3_bucket.name = (Some(bucket_name.to_string())); + s3_bucket.owner_identity = { + let mut s3_user_identity = S3UserIdentity::default(); + s3_user_identity.principal_id = Some(String::default()); + Some(s3_user_identity) }; - - return S3EventRecord { - event_version: (Some(String::default())), - event_source: (Some(String::default())), - aws_region: (Some(String::default())), - event_time: (chrono::DateTime::default()), - event_name: (Some(event_name.to_string())), - principal_id: (S3UserIdentity { - principal_id: (Some("X".to_string())), - }), - request_parameters: (S3RequestParameters { - source_ip_address: (Some(String::default())), - }), - response_elements: (HashMap::new()), - s3: (s3_entity), + s3_bucket.arn = Some(String::default()); + + let mut s3_object = S3Object::default(); + s3_object.key = Some(object_key.to_string()); + s3_object.size = Some(1); + s3_object.url_decoded_key = Some(String::default()); + s3_object.version_id = Some(String::default()); + s3_object.e_tag = Some(String::default()); + s3_object.sequencer = Some(String::default()); + + let mut s3_entity = S3Entity::default(); + s3_entity.schema_version = Some(String::default()); + s3_entity.configuration_id = Some(String::default()); + s3_entity.bucket = s3_bucket; + s3_entity.object = s3_object; + + let mut s3_event_record = S3EventRecord::default(); + s3_event_record.event_version = Some(String::default()); + s3_event_record.event_source = Some(String::default()); + s3_event_record.aws_region = Some(String::default()); + s3_event_record.event_time = chrono::DateTime::default(); + s3_event_record.event_name = Some(event_name.to_string()); + s3_event_record.principal_id = { + let mut s3_user_identity = S3UserIdentity::default(); + s3_user_identity.principal_id = Some("X".to_string()); + s3_user_identity + }; + s3_event_record.request_parameters = { + let mut s3_request_parameters = S3RequestParameters::default(); + s3_request_parameters.source_ip_address = Some(String::default()); + s3_request_parameters }; + s3_event_record.response_elements = HashMap::new(); + s3_event_record.s3 = s3_entity; + s3_event_record } } diff --git a/examples/basic-s3-thumbnail/src/s3.rs b/examples/basic-s3-thumbnail/src/s3.rs index 0dd8629d9..1a7593710 100644 --- a/examples/basic-s3-thumbnail/src/s3.rs +++ b/examples/basic-s3-thumbnail/src/s3.rs @@ -1,7 +1,5 @@ use async_trait::async_trait; -use aws_sdk_s3::operation::get_object::GetObjectError; -use aws_sdk_s3::Client as S3Client; -use aws_smithy_http::byte_stream::ByteStream; +use aws_sdk_s3::{operation::get_object::GetObjectError, primitives::ByteStream, Client as S3Client}; use lambda_runtime::tracing; #[async_trait] @@ -45,7 +43,7 @@ impl PutFile for S3Client { let result = self.put_object().bucket(bucket).key(key).body(bytes).send().await; match result { - Ok(_) => Ok(format!("Uploaded a file with key {} into {}", key, bucket)), + Ok(_) => Ok(format!("Uploaded a file with key {key} into {bucket}")), Err(err) => Err(err.into_service_error().meta().message().unwrap().to_string()), } } diff --git a/examples/basic-sdk/Cargo.toml b/examples/basic-sdk/Cargo.toml index 454a970f4..6e680aaf9 100644 --- a/examples/basic-sdk/Cargo.toml +++ b/examples/basic-sdk/Cargo.toml @@ -7,12 +7,11 @@ edition = "2021" [dependencies] async-trait = "0.1" -aws-config = "0.54" -aws-sdk-s3 = "0.24" +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +aws-sdk-s3 = "1.96.0" lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.136" +serde = "1.0.219" tokio = { version = "1", features = ["macros"] } [dev-dependencies] -mockall = "0.11.3" -tokio-test = "0.4.2" \ No newline at end of file +mockall = "0.13.1" diff --git a/examples/basic-sdk/src/main.rs b/examples/basic-sdk/src/main.rs index d49c84e10..a021f4c20 100644 --- a/examples/basic-sdk/src/main.rs +++ b/examples/basic-sdk/src/main.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use aws_sdk_s3::{output::ListObjectsV2Output, Client as S3Client}; +use aws_sdk_s3::{operation::list_objects_v2::ListObjectsV2Output, Client as S3Client}; use lambda_runtime::{service_fn, tracing, Error, LambdaEvent}; use serde::{Deserialize, Serialize}; @@ -52,8 +52,7 @@ async fn my_handler(event: LambdaEvent, client: &T) -> let objects_rsp = client.list_objects(&bucket).await?; let objects: Vec<_> = objects_rsp .contents() - .ok_or("missing objects in list-objects-v2 response")? - .into_iter() + .iter() .filter_map(|o| o.key().map(|k| k.to_string())) .collect(); @@ -71,7 +70,7 @@ async fn my_handler(event: LambdaEvent, client: &T) -> #[cfg(test)] mod tests { use super::*; - use aws_sdk_s3::model::Object; + use aws_sdk_s3::types::Object; use lambda_runtime::{Context, LambdaEvent}; use mockall::predicate::eq; diff --git a/examples/basic-shared-resource/Cargo.toml b/examples/basic-shared-resource/Cargo.toml index 2aad58865..514bef32c 100644 --- a/examples/basic-shared-resource/Cargo.toml +++ b/examples/basic-shared-resource/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.136" +serde = "1.0.219" tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-sqs/Cargo.toml b/examples/basic-sqs/Cargo.toml index 0df7d8e22..36efee366 100644 --- a/examples/basic-sqs/Cargo.toml +++ b/examples/basic-sqs/Cargo.toml @@ -17,5 +17,5 @@ edition = "2021" [dependencies] aws_lambda_events = { path = "../../lambda-events" } lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.136" +serde = "1.0.219" tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-streaming-response/src/main.rs b/examples/basic-streaming-response/src/main.rs index 8533c8e33..fe112b803 100644 --- a/examples/basic-streaming-response/src/main.rs +++ b/examples/basic-streaming-response/src/main.rs @@ -7,7 +7,7 @@ use serde_json::Value; use std::{thread, time::Duration}; async fn func(_event: LambdaEvent) -> Result, Error> { - let messages = vec!["Hello", "world", "from", "Lambda!"]; + let messages = ["Hello", "world", "from", "Lambda!"]; let (mut tx, rx) = channel(); diff --git a/examples/extension-basic/Cargo.toml b/examples/extension-basic/Cargo.toml index 48e2ed51b..d7cf1f135 100644 --- a/examples/extension-basic/Cargo.toml +++ b/examples/extension-basic/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros"] } diff --git a/examples/extension-combined/Cargo.toml b/examples/extension-combined/Cargo.toml index 2a745c7b6..93aacca10 100644 --- a/examples/extension-combined/Cargo.toml +++ b/examples/extension-combined/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros"] } diff --git a/examples/extension-custom-events/Cargo.toml b/examples/extension-custom-events/Cargo.toml index c2f813c39..dfef4c4b0 100644 --- a/examples/extension-custom-events/Cargo.toml +++ b/examples/extension-custom-events/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros"] } diff --git a/examples/extension-custom-service/Cargo.toml b/examples/extension-custom-service/Cargo.toml index b51eae8e6..8d0e4575d 100644 --- a/examples/extension-custom-service/Cargo.toml +++ b/examples/extension-custom-service/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros"] } diff --git a/examples/extension-internal-flush/Cargo.toml b/examples/extension-internal-flush/Cargo.toml index daadd0eb0..1a0747dd8 100644 --- a/examples/extension-internal-flush/Cargo.toml +++ b/examples/extension-internal-flush/Cargo.toml @@ -8,5 +8,5 @@ anyhow = "1" aws_lambda_events = { path = "../../lambda-events" } lambda-extension = { path = "../../lambda-extension" } lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.136" -tokio = { version = "1", features = ["macros", "sync"] } +serde = "1.0.219" +tokio = { version = "1.46", features = ["macros", "sync"] } diff --git a/examples/extension-internal-flush/README.md b/examples/extension-internal-flush/README.md index 553f7a3d6..c64086767 100644 --- a/examples/extension-internal-flush/README.md +++ b/examples/extension-internal-flush/README.md @@ -18,9 +18,10 @@ the Lambda service returns the response to the caller immediately. Extensions ma without introducing an observable delay. ## Build & Deploy +Two deploy options for internal extensions: 1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation) -2. Build the extension with `cargo lambda build --release` +2. Build a function with the internal extension embedded with `cargo lambda build --release` 3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE` The last command will give you an ARN for the extension layer that you can use in your functions. @@ -28,3 +29,18 @@ The last command will give you an ARN for the extension layer that you can use i ## Build for ARM 64 Build the extension with `cargo lambda build --release --arm64` + + +## Test your Function + +From your local: +``` +cargo lambda watch +# in new terminal window +cargo lambda invoke --data-ascii '{"Records":[{"messageId":"MessageID_1","receiptHandle":"MessageReceiptHandle","body":"{\"a\":\"Test\",\"b\":123}"}]}' +``` + +If you have deployed to AWS using the commands in the 'Build & Deploy' section, you can also invoke your function remotely: +``` +cargo lambda invoke extension-internal-flush --remote --data-ascii '{"Records":[{"messageId":"MessageID_1","receiptHandle":"MessageReceiptHandle","body":"{\"a\":\"Test\",\"b\":123}"}]}' +``` \ No newline at end of file diff --git a/examples/extension-internal-flush/src/main.rs b/examples/extension-internal-flush/src/main.rs index ff1d10dab..d20213e15 100644 --- a/examples/extension-internal-flush/src/main.rs +++ b/examples/extension-internal-flush/src/main.rs @@ -2,8 +2,10 @@ use anyhow::anyhow; use aws_lambda_events::sqs::{SqsBatchResponse, SqsEventObj}; use lambda_extension::{service_fn, tracing, Error, Extension, NextEvent}; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::sync::Mutex; +use tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex, +}; use std::sync::Arc; @@ -102,6 +104,10 @@ async fn main() -> Result<(), Error> { let handler = Arc::new(EventHandler::new(request_done_sender)); tokio::try_join!( + // always poll the handler function first before the flush extension, + // this results in a smaller future due to not needing to track which was polled first + // each time, and also a tiny latency savings + biased; lambda_runtime::run(service_fn(|event| { let handler = handler.clone(); async move { handler.invoke(event).await } diff --git a/examples/extension-logs-basic/Cargo.toml b/examples/extension-logs-basic/Cargo.toml index dccc1ec0c..230ebc7e0 100644 --- a/examples/extension-logs-basic/Cargo.toml +++ b/examples/extension-logs-basic/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros", "rt"] } diff --git a/examples/extension-logs-custom-service/Cargo.toml b/examples/extension-logs-custom-service/Cargo.toml index 1b1eea0a2..421fe9ff3 100644 --- a/examples/extension-logs-custom-service/Cargo.toml +++ b/examples/extension-logs-custom-service/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros"] } diff --git a/examples/extension-logs-kinesis-firehose/Cargo.toml b/examples/extension-logs-kinesis-firehose/Cargo.toml index c6675e5aa..84f7ac96e 100644 --- a/examples/extension-logs-kinesis-firehose/Cargo.toml +++ b/examples/extension-logs-kinesis-firehose/Cargo.toml @@ -5,6 +5,6 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -tokio = { version = "1.17.0", features = ["full"] } -aws-config = "0.13.0" -aws-sdk-firehose = "0.13.0" +tokio = { version = "1.46.1", features = ["full"] } +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +aws-sdk-firehose = "1.82.0" diff --git a/examples/extension-logs-kinesis-firehose/src/main.rs b/examples/extension-logs-kinesis-firehose/src/main.rs index 7871ce526..c9d8a2e47 100644 --- a/examples/extension-logs-kinesis-firehose/src/main.rs +++ b/examples/extension-logs-kinesis-firehose/src/main.rs @@ -1,4 +1,4 @@ -use aws_sdk_firehose::{model::Record, types::Blob, Client}; +use aws_sdk_firehose::{primitives::Blob, types::Record, Client}; use lambda_extension::{tracing, Error, Extension, LambdaLog, LambdaLogRecord, Service, SharedService}; use std::{future::Future, pin::Pin, task::Poll}; @@ -31,7 +31,12 @@ impl Service> for FirehoseLogsProcessor { for log in logs { match log.record { LambdaLogRecord::Function(record) => { - records.push(Record::builder().data(Blob::new(record.as_bytes())).build()) + match Record::builder().data(Blob::new(record.as_bytes())).build() { + Ok(rec) => records.push(rec), + Err(e) => { + return Box::pin(async move { Err(e.into()) }); + } + } } _ => unreachable!(), } diff --git a/examples/extension-telemetry-basic/Cargo.toml b/examples/extension-telemetry-basic/Cargo.toml index 1b8b1ba4f..a0fb6b875 100644 --- a/examples/extension-telemetry-basic/Cargo.toml +++ b/examples/extension-telemetry-basic/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda-extension = { path = "../../lambda-extension" } -serde = "1.0.136" tokio = { version = "1", features = ["macros", "rt"] } diff --git a/examples/http-axum-apigw-authorizer/Cargo.toml b/examples/http-axum-apigw-authorizer/Cargo.toml index c757aa942..44c50167a 100644 --- a/examples/http-axum-apigw-authorizer/Cargo.toml +++ b/examples/http-axum-apigw-authorizer/Cargo.toml @@ -4,9 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7" +axum = "0.8" lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.196" serde_json = "1.0" tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-axum-apigw-authorizer/src/main.rs b/examples/http-axum-apigw-authorizer/src/main.rs index 513a6cd8e..8adb90240 100644 --- a/examples/http-axum-apigw-authorizer/src/main.rs +++ b/examples/http-axum-apigw-authorizer/src/main.rs @@ -1,5 +1,4 @@ use axum::{ - async_trait, extract::{FromRequest, Request}, http::StatusCode, response::Json, @@ -13,7 +12,6 @@ use std::{collections::HashMap, env::set_var}; struct AuthorizerField(String); struct AuthorizerFields(HashMap); -#[async_trait] impl FromRequest for AuthorizerField where S: Send + Sync, @@ -30,7 +28,6 @@ where } } -#[async_trait] impl FromRequest for AuthorizerFields where S: Send + Sync, diff --git a/examples/http-axum-diesel-ssl/Cargo.toml b/examples/http-axum-diesel-ssl/Cargo.toml index 693669576..d21df0b95 100755 --- a/examples/http-axum-diesel-ssl/Cargo.toml +++ b/examples/http-axum-diesel-ssl/Cargo.toml @@ -4,16 +4,14 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7" -bb8 = "0.8.0" -diesel = "2.0.3" -diesel-async = { version = "0.2.1", features = ["postgres", "bb8"] } +axum = "0.8" +diesel = "2.2.11" +diesel-async = { version = "0.6.1", features = ["postgres", "bb8"] } lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.159" -futures-util = "0.3.21" -rustls = "0.20.8" -rustls-native-certs = "0.6.2" -tokio = { version = "1.2.0", default-features = false, features = ["macros", "rt-multi-thread"] } -tokio-postgres = "0.7.7" -tokio-postgres-rustls = "0.9.0" \ No newline at end of file +serde = "1.0.219" +futures-util = "0.3.31" +rustls = "0.23.28" +rustls-native-certs = "0.8.1" +tokio = { version = "1.46.1", default-features = false, features = ["macros", "rt-multi-thread"] } +tokio-postgres = "0.7.13" +tokio-postgres-rustls = "0.13.0" diff --git a/examples/http-axum-diesel-ssl/src/main.rs b/examples/http-axum-diesel-ssl/src/main.rs index b340b44d1..395c18433 100755 --- a/examples/http-axum-diesel-ssl/src/main.rs +++ b/examples/http-axum-diesel-ssl/src/main.rs @@ -1,19 +1,18 @@ -use diesel::{ConnectionError, ConnectionResult}; -use futures_util::future::BoxFuture; -use futures_util::FutureExt; -use std::time::Duration; - use axum::{ extract::{Path, State}, response::Json, routing::get, Router, }; -use bb8::Pool; -use diesel::prelude::*; -use diesel_async::{pooled_connection::AsyncDieselConnectionManager, AsyncPgConnection, RunQueryDsl}; +use diesel::{prelude::*, ConnectionError, ConnectionResult}; +use diesel_async::{ + pooled_connection::{bb8::Pool, AsyncDieselConnectionManager, ManagerConfig}, + AsyncPgConnection, RunQueryDsl, +}; +use futures_util::{future::BoxFuture, FutureExt}; use lambda_http::{http::StatusCode, run, tracing, Error}; use serde::{Deserialize, Serialize}; +use std::time::Duration; table! { posts (id) { @@ -40,7 +39,7 @@ struct NewPost { published: bool, } -type AsyncPool = Pool>; +type AsyncPool = Pool; type ServerError = (StatusCode, String); async fn create_post(State(pool): State, Json(post): Json) -> Result, ServerError> { @@ -104,7 +103,10 @@ async fn main() -> Result<(), Error> { // Format for DATABASE_URL=postgres://your_username:your_password@your_host:5432/your_db?sslmode=require let db_url = std::env::var("DATABASE_URL").expect("Env var `DATABASE_URL` not set"); - let mgr = AsyncDieselConnectionManager::::new_with_setup(db_url, establish_connection); + let mut config = ManagerConfig::default(); + config.custom_setup = Box::new(establish_connection); + + let mgr = AsyncDieselConnectionManager::::new_with_config(db_url, config); let pool = Pool::builder() .max_size(10) @@ -129,19 +131,15 @@ fn establish_connection(config: &str) -> BoxFuture BoxFuture rustls::RootCertStore { let mut roots = rustls::RootCertStore::empty(); let certs = rustls_native_certs::load_native_certs().expect("Certs not loadable!"); - let certs: Vec<_> = certs.into_iter().map(|cert| cert.0).collect(); - roots.add_parsable_certificates(&certs); + roots.add_parsable_certificates(certs); roots } diff --git a/examples/http-axum-diesel/Cargo.toml b/examples/http-axum-diesel/Cargo.toml index 39fc813e4..bed367623 100644 --- a/examples/http-axum-diesel/Cargo.toml +++ b/examples/http-axum-diesel/Cargo.toml @@ -4,11 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7" -bb8 = "0.8.0" -diesel = "2.0.3" -diesel-async = { version = "0.2.1", features = ["postgres", "bb8"] } +axum = "0.8" +diesel = "2.2.11" +diesel-async = { version = "0.6.1", features = ["postgres", "bb8"] } lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.159" +serde = "1.0.219" tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-axum-diesel/src/main.rs b/examples/http-axum-diesel/src/main.rs index b7247be47..348d75357 100644 --- a/examples/http-axum-diesel/src/main.rs +++ b/examples/http-axum-diesel/src/main.rs @@ -4,9 +4,11 @@ use axum::{ routing::get, Router, }; -use bb8::Pool; use diesel::prelude::*; -use diesel_async::{pooled_connection::AsyncDieselConnectionManager, AsyncPgConnection, RunQueryDsl}; +use diesel_async::{ + pooled_connection::{bb8::Pool, AsyncDieselConnectionManager}, + AsyncPgConnection, RunQueryDsl, +}; use lambda_http::{http::StatusCode, run, tracing, Error}; use serde::{Deserialize, Serialize}; @@ -35,7 +37,7 @@ struct NewPost { published: bool, } -type AsyncPool = Pool>; +type AsyncPool = Pool; type ServerError = (StatusCode, String); async fn create_post(State(pool): State, Json(post): Json) -> Result, ServerError> { diff --git a/examples/http-axum-middleware/Cargo.toml b/examples/http-axum-middleware/Cargo.toml index 228fc0ae4..f39669411 100644 --- a/examples/http-axum-middleware/Cargo.toml +++ b/examples/http-axum-middleware/Cargo.toml @@ -4,10 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7" +axum = "0.8" lambda_http = { path = "../../lambda-http", default-features = false, features = [ "apigw_rest", "tracing" ] } -lambda_runtime = { path = "../../lambda-runtime" } serde_json = "1.0" tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-axum-middleware/src/main.rs b/examples/http-axum-middleware/src/main.rs index b1e92811a..e335c270f 100644 --- a/examples/http-axum-middleware/src/main.rs +++ b/examples/http-axum-middleware/src/main.rs @@ -10,8 +10,7 @@ //! ``` use axum::{response::Json, routing::post, Router}; -use lambda_http::request::RequestContext::ApiGatewayV1; -use lambda_http::{run, tracing, Error}; +use lambda_http::{request::RequestContext::ApiGatewayV1, run, tracing, Error}; use serde_json::{json, Value}; // Sample middleware that logs the request id diff --git a/examples/http-axum-streaming-otel/Cargo.toml b/examples/http-axum-streaming-otel/Cargo.toml new file mode 100644 index 000000000..d917bb033 --- /dev/null +++ b/examples/http-axum-streaming-otel/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "http-axum-streaming-otel" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.8" +bytes = "1" +lambda_http = { path = "../../lambda-http", default-features = false, features = [ + "apigw_http", "tracing", "opentelemetry" +] } +opentelemetry = "0.30" +opentelemetry_sdk = { version = "0.30", features = ["rt-tokio"] } +opentelemetry-stdout = { version = "0.30", features = ["trace"] } +thiserror = "2.0" +tokio = { version = "1", features = ["macros"] } +tokio-stream = "0.1.2" +tracing = "0.1" +tracing-opentelemetry = "0.31" +tracing-subscriber = "0.3" diff --git a/examples/http-axum-streaming-otel/README.md b/examples/http-axum-streaming-otel/README.md new file mode 100644 index 000000000..194fe4e4b --- /dev/null +++ b/examples/http-axum-streaming-otel/README.md @@ -0,0 +1,25 @@ +# AWS Lambda Function example + +This example shows how to build a **streaming HTTP response** with `Axum` and +run it on AWS Lambda using a custom runtime with OpenTelemetry (OTel) support. + +Tracing data is exported as console log entries visible in CloudWatch. Note that +CloudWatch assigns a `Timestamp` to each log entry based on when it receives the +data (batch exported). To see when work actually occurred, look at the span's +event attributes, which include the precise local timestamps of those events. + +## Build & Deploy + +1. Install + [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation) +2. Build the function with `cargo lambda build --release` +3. Deploy the function to AWS Lambda with: + - `cargo lambda deploy --enable-function-url --iam-role YOUR_ROLE` to stream words +4. Enable Lambda streaming response on Lambda console: change the function url's + invoke mode to `RESPONSE_STREAM` +5. Verify the function works: `curl -N `. The results should be + streamed back with 0.5 second pause between each word. + +## Build for ARM 64 + +Build the function with `cargo lambda build --release --arm64` diff --git a/examples/http-axum-streaming-otel/src/main.rs b/examples/http-axum-streaming-otel/src/main.rs new file mode 100644 index 000000000..64f4e49e4 --- /dev/null +++ b/examples/http-axum-streaming-otel/src/main.rs @@ -0,0 +1,106 @@ +//! # Example: Axum Streaming Responses on AWS Lambda with OTel +//! +//! Demonstrates serving **incremental streaming responses** from Axum handlers +//! running in AWS Lambda using a **custom** `lambda_runtime::Runtime` with +//! OpenTelemetry (OTel) support. +//! +//! - Runs with a custom `Runtime` + `StreamAdapter`, which convert Axum +//! responses into streaming bodies delivered as data is produced (unlike the +//! default `run_with_streaming_response` helper). + +use axum::{ + body::Body, + http::{ + self, + header::{CACHE_CONTROL, CONTENT_TYPE}, + StatusCode, + }, + response::{IntoResponse, Response}, + routing::get, + Router, +}; +use bytes::Bytes; +use core::{convert::Infallible, time::Duration}; +use lambda_http::{ + lambda_runtime::{ + layers::{OpenTelemetryFaasTrigger, OpenTelemetryLayer as OtelLayer}, + tracing::Instrument, + Runtime, + }, + tracing, Error, StreamAdapter, +}; +use opentelemetry::trace::TracerProvider; +use opentelemetry_sdk::trace; +use thiserror::Error; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; +use tracing_subscriber::prelude::*; + +#[derive(Debug, Error)] +pub enum AppError { + #[error("{0}")] + Http(#[from] http::Error), +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response() + } +} + +#[tracing::instrument(skip_all)] +async fn stream_words() -> Result { + let (tx, rx) = mpsc::channel::>(8); + let body = Body::from_stream(ReceiverStream::new(rx)); + + tokio::spawn( + async move { + for (idx, msg) in ["Hello", "world", "from", "Lambda!"].iter().enumerate() { + tokio::time::sleep(Duration::from_millis(500)).await; + let line = format!("{msg}\n"); + tracing::info!(chunk.idx = idx, bytes = line.len(), "emit"); + if tx.send(Ok(Bytes::from(line))).await.is_err() { + break; + } + } + } + .instrument(tracing::info_span!("producer.stream_words")), + ); + + Ok(Response::builder() + .status(StatusCode::OK) + .header(CONTENT_TYPE, "text/plain; charset=utf-8") + .header(CACHE_CONTROL, "no-cache") + .body(body)?) +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + // Set up OpenTelemetry tracer provider that writes spans to stdout for + // debugging purposes + let exporter = opentelemetry_stdout::SpanExporter::default(); + let tracer_provider = trace::SdkTracerProvider::builder() + .with_batch_exporter(exporter) + .build(); + + // Set up link between OpenTelemetry and tracing crate + tracing_subscriber::registry() + .with(tracing_opentelemetry::OpenTelemetryLayer::new( + tracer_provider.tracer("my-streaming-app"), + )) + .init(); + + let svc = Router::new().route("/", get(stream_words)); + + // Initialize the Lambda runtime and add OpenTelemetry tracing + let runtime = Runtime::new(StreamAdapter::from(svc)).layer( + OtelLayer::new(|| { + if let Err(err) = tracer_provider.force_flush() { + eprintln!("Error flushing traces: {err:#?}"); + } + }) + .with_trigger(OpenTelemetryFaasTrigger::Http), + ); + + runtime.run().await +} diff --git a/examples/http-axum-streaming/Cargo.toml b/examples/http-axum-streaming/Cargo.toml new file mode 100644 index 000000000..a951562b0 --- /dev/null +++ b/examples/http-axum-streaming/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "http-axum-streaming" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.8" +bytes = "1" +lambda_http = { path = "../../lambda-http", default-features = false, features = [ + "apigw_http", "tracing" +] } +thiserror = "2.0" +tokio = { version = "1", features = ["macros"] } +tokio-stream = "0.1.2" diff --git a/examples/http-axum-streaming/README.md b/examples/http-axum-streaming/README.md new file mode 100644 index 000000000..fe7e573d7 --- /dev/null +++ b/examples/http-axum-streaming/README.md @@ -0,0 +1,20 @@ +# AWS Lambda Function example + +This example demonstrates building a **streaming** HTTP response with Axum, +deployed on AWS Lambda using a custom runtime. + +## Build & Deploy + +1. Install + [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation) +2. Build the function with `cargo lambda build --release` +3. Deploy the function to AWS Lambda with: + - `cargo lambda deploy --enable-function-url --iam-role YOUR_ROLE` to stream words +4. Enable Lambda streaming response on Lambda console: change the function url's + invoke mode to `RESPONSE_STREAM` +5. Verify the function works: `curl -N `. The results should be + streamed back with 0.5 second pause between each word. + +## Build for ARM 64 + +Build the function with `cargo lambda build --release --arm64` diff --git a/examples/http-axum-streaming/src/main.rs b/examples/http-axum-streaming/src/main.rs new file mode 100644 index 000000000..1812f8795 --- /dev/null +++ b/examples/http-axum-streaming/src/main.rs @@ -0,0 +1,70 @@ +//! # Example: Axum Streaming Responses on AWS Lambda +//! +//! Demonstrates serving **incremental streaming responses** from Axum handlers +//! running in AWS Lambda. +//! +//! - Runs with `run_with_streaming_response`, which uses the **default Lambda +//! runtime** to convert Axum responses into streaming bodies delivered as +//! data is produced (unlike the OTel example, which used a custom `Runtime` + +//! `StreamAdapter`). + +use axum::{ + body::Body, + http::{ + self, + header::{CACHE_CONTROL, CONTENT_TYPE}, + StatusCode, + }, + response::{IntoResponse, Response}, + routing::get, + Router, +}; +use bytes::Bytes; +use core::{convert::Infallible, time::Duration}; +use lambda_http::{run_with_streaming_response, tracing, Error}; +use thiserror::Error; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; + +#[derive(Debug, Error)] +pub enum AppError { + #[error("{0}")] + Http(#[from] http::Error), +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response() + } +} + +async fn stream_words() -> Result { + let (tx, rx) = mpsc::channel::>(8); + let body = Body::from_stream(ReceiverStream::new(rx)); + + tokio::spawn(async move { + for msg in ["Hello", "world", "from", "Lambda!"] { + tokio::time::sleep(Duration::from_millis(500)).await; + if tx.send(Ok(Bytes::from(format!("{msg}\n")))).await.is_err() { + break; + } + } + }); + + Ok(Response::builder() + .status(StatusCode::OK) + .header(CONTENT_TYPE, "text/plain; charset=utf-8") + .header(CACHE_CONTROL, "no-cache") + .body(body)?) +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + tracing::init_default_subscriber(); + + let svc = Router::new().route("/", get(stream_words)); + + // Automatically convert the service into a streaming response with a + // default runtime. + run_with_streaming_response(svc).await +} diff --git a/examples/http-axum/Cargo.toml b/examples/http-axum/Cargo.toml index 7ab3c0ec4..7664e7a79 100644 --- a/examples/http-axum/Cargo.toml +++ b/examples/http-axum/Cargo.toml @@ -4,9 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7" +axum = "0.8" lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } -serde = "1.0.196" +serde = "1.0.219" serde_json = "1.0" tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-axum/cdk/package-lock.json b/examples/http-axum/cdk/package-lock.json index 9a7e30267..a7df29262 100644 --- a/examples/http-axum/cdk/package-lock.json +++ b/examples/http-axum/cdk/package-lock.json @@ -8,7 +8,7 @@ "name": "cdk", "version": "0.1.0", "dependencies": { - "aws-cdk-lib": "^2.126.0", + "aws-cdk-lib": "^2.193.0", "cargo-lambda-cdk": "^0.0.17", "constructs": "^10.0.0", "source-map-support": "^0.5.21" @@ -40,19 +40,52 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.202", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", - "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==" - }, - "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" + "version": "2.2.230", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.230.tgz", + "integrity": "sha512-kUnhKIYu42hqBa6a8x2/7o29ObpJgjYGQy28lZDq9awXyvpR62I2bRxrNKNR3uFUQz3ySuT9JXhGHhuZPdbnFw==", + "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", - "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", + "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/cloud-assembly-schema": { + "version": "41.2.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-41.2.0.tgz", + "integrity": "sha512-JaulVS6z9y5+u4jNmoWbHZRs9uGOnmn/ktXygNWKNu1k6lF3ad4so3s18eRu15XCbUIomxN9WPYT6Ehh7hzONw==", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "license": "Apache-2.0", + "dependencies": { + "jsonschema": "~1.4.1", + "semver": "^7.7.1" + }, + "engines": { + "node": ">= 14.15.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { + "version": "7.7.1", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, "node_modules/@babel/code-frame": { "version": "7.23.5", @@ -1315,9 +1348,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.126.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.126.0.tgz", - "integrity": "sha512-HlwBYiqNCfhPUyozjoV4zJIBgH0adE/zTVATmeaM1jcNGs6jwkPMpWsF75JuXOoVtkcfpBFyNKloGd82nTiB0A==", + "version": "2.193.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.193.0.tgz", + "integrity": "sha512-Bsf11FM85+s9jSAT8JfDNlrSz6LF3Xa2eSNOyMLcXNopI7eVXP1U6opRHK0waaZGLmQfOwsbSp/9XRMKikkazg==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -1328,21 +1361,24 @@ "punycode", "semver", "table", - "yaml" + "yaml", + "mime-types" ], + "license": "Apache-2.0", "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.202", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", - "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", + "@aws-cdk/asset-awscli-v1": "^2.2.229", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", + "@aws-cdk/cloud-assembly-schema": "^41.0.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.2.0", - "ignore": "^5.3.0", - "jsonschema": "^1.4.1", + "fs-extra": "^11.3.0", + "ignore": "^5.3.2", + "jsonschema": "^1.5.0", + "mime-types": "^2.1.35", "minimatch": "^3.1.2", "punycode": "^2.3.1", - "semver": "^7.5.4", - "table": "^6.8.1", + "semver": "^7.7.1", + "table": "^6.9.0", "yaml": "1.10.2" }, "engines": { @@ -1358,14 +1394,14 @@ "license": "Apache-2.0" }, "node_modules/aws-cdk-lib/node_modules/ajv": { - "version": "8.12.0", + "version": "8.17.1", "inBundle": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -1455,8 +1491,23 @@ "inBundle": true, "license": "MIT" }, + "node_modules/aws-cdk-lib/node_modules/fast-uri": { + "version": "3.0.6", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.2.0", + "version": "11.3.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -1474,7 +1525,7 @@ "license": "ISC" }, "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.3.0", + "version": "5.3.2", "inBundle": true, "license": "MIT", "engines": { @@ -1506,7 +1557,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/jsonschema": { - "version": "1.4.1", + "version": "1.5.0", "inBundle": true, "license": "MIT", "engines": { @@ -1518,15 +1569,23 @@ "inBundle": true, "license": "MIT" }, - "node_modules/aws-cdk-lib/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", "inBundle": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "inBundle": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, "node_modules/aws-cdk-lib/node_modules/minimatch": { @@ -1557,12 +1616,9 @@ } }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.5.4", + "version": "7.7.1", "inBundle": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -1611,7 +1667,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/table": { - "version": "6.8.1", + "version": "6.9.0", "inBundle": true, "license": "BSD-3-Clause", "dependencies": { @@ -1633,19 +1689,6 @@ "node": ">= 10.0.0" } }, - "node_modules/aws-cdk-lib/node_modules/uri-js": { - "version": "4.4.1", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" - }, "node_modules/aws-cdk-lib/node_modules/yaml": { "version": "1.10.2", "inBundle": true, @@ -4408,19 +4451,33 @@ } }, "@aws-cdk/asset-awscli-v1": { - "version": "2.2.202", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", - "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==" - }, - "@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" + "version": "2.2.230", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.230.tgz", + "integrity": "sha512-kUnhKIYu42hqBa6a8x2/7o29ObpJgjYGQy28lZDq9awXyvpR62I2bRxrNKNR3uFUQz3ySuT9JXhGHhuZPdbnFw==" }, "@aws-cdk/asset-node-proxy-agent-v6": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", - "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", + "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==" + }, + "@aws-cdk/cloud-assembly-schema": { + "version": "41.2.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-41.2.0.tgz", + "integrity": "sha512-JaulVS6z9y5+u4jNmoWbHZRs9uGOnmn/ktXygNWKNu1k6lF3ad4so3s18eRu15XCbUIomxN9WPYT6Ehh7hzONw==", + "requires": { + "jsonschema": "~1.4.1", + "semver": "^7.7.1" + }, + "dependencies": { + "jsonschema": { + "version": "1.4.1", + "bundled": true + }, + "semver": { + "version": "7.7.1", + "bundled": true + } + } }, "@babel/code-frame": { "version": "7.23.5", @@ -5423,22 +5480,23 @@ } }, "aws-cdk-lib": { - "version": "2.126.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.126.0.tgz", - "integrity": "sha512-HlwBYiqNCfhPUyozjoV4zJIBgH0adE/zTVATmeaM1jcNGs6jwkPMpWsF75JuXOoVtkcfpBFyNKloGd82nTiB0A==", + "version": "2.193.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.193.0.tgz", + "integrity": "sha512-Bsf11FM85+s9jSAT8JfDNlrSz6LF3Xa2eSNOyMLcXNopI7eVXP1U6opRHK0waaZGLmQfOwsbSp/9XRMKikkazg==", "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.202", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", - "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", + "@aws-cdk/asset-awscli-v1": "^2.2.229", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", + "@aws-cdk/cloud-assembly-schema": "^41.0.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.2.0", - "ignore": "^5.3.0", - "jsonschema": "^1.4.1", + "fs-extra": "^11.3.0", + "ignore": "^5.3.2", + "jsonschema": "^1.5.0", + "mime-types": "^2.1.35", "minimatch": "^3.1.2", "punycode": "^2.3.1", - "semver": "^7.5.4", - "table": "^6.8.1", + "semver": "^7.7.1", + "table": "^6.9.0", "yaml": "1.10.2" }, "dependencies": { @@ -5447,13 +5505,13 @@ "bundled": true }, "ajv": { - "version": "8.12.0", + "version": "8.17.1", "bundled": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "ansi-regex": { @@ -5510,8 +5568,12 @@ "version": "3.1.3", "bundled": true }, + "fast-uri": { + "version": "3.0.6", + "bundled": true + }, "fs-extra": { - "version": "11.2.0", + "version": "11.3.0", "bundled": true, "requires": { "graceful-fs": "^4.2.0", @@ -5524,7 +5586,7 @@ "bundled": true }, "ignore": { - "version": "5.3.0", + "version": "5.3.2", "bundled": true }, "is-fullwidth-code-point": { @@ -5544,18 +5606,22 @@ } }, "jsonschema": { - "version": "1.4.1", + "version": "1.5.0", "bundled": true }, "lodash.truncate": { "version": "4.4.2", "bundled": true }, - "lru-cache": { - "version": "6.0.0", + "mime-db": { + "version": "1.52.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.35", "bundled": true, "requires": { - "yallist": "^4.0.0" + "mime-db": "1.52.0" } }, "minimatch": { @@ -5574,11 +5640,8 @@ "bundled": true }, "semver": { - "version": "7.5.4", - "bundled": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.1", + "bundled": true }, "slice-ansi": { "version": "4.0.0", @@ -5606,7 +5669,7 @@ } }, "table": { - "version": "6.8.1", + "version": "6.9.0", "bundled": true, "requires": { "ajv": "^8.0.1", @@ -5620,17 +5683,6 @@ "version": "2.0.1", "bundled": true }, - "uri-js": { - "version": "4.4.1", - "bundled": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "yallist": { - "version": "4.0.0", - "bundled": true - }, "yaml": { "version": "1.10.2", "bundled": true diff --git a/examples/http-axum/cdk/package.json b/examples/http-axum/cdk/package.json index 9117d19fc..05bcdc498 100644 --- a/examples/http-axum/cdk/package.json +++ b/examples/http-axum/cdk/package.json @@ -20,7 +20,7 @@ "typescript": "~5.3.3" }, "dependencies": { - "aws-cdk-lib": "^2.126.0", + "aws-cdk-lib": "^2.193.0", "cargo-lambda-cdk": "^0.0.17", "constructs": "^10.0.0", "source-map-support": "^0.5.21" diff --git a/examples/http-axum/src/main.rs b/examples/http-axum/src/main.rs index dcd5d1541..3b00fc57c 100644 --- a/examples/http-axum/src/main.rs +++ b/examples/http-axum/src/main.rs @@ -6,10 +6,9 @@ //! implementation to the Lambda runtime to run as a Lambda function. By using Axum instead //! of a basic `tower::Service` you get web framework niceties like routing, request component //! extraction, validation, etc. -use axum::extract::Query; -use axum::http::StatusCode; use axum::{ - extract::Path, + extract::{Path, Query}, + http::StatusCode, response::Json, routing::{get, post}, Router, diff --git a/examples/http-basic-lambda/Cargo.toml b/examples/http-basic-lambda/Cargo.toml index c7a515072..2f2523894 100644 --- a/examples/http-basic-lambda/Cargo.toml +++ b/examples/http-basic-lambda/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-basic-lambda/src/main.rs b/examples/http-basic-lambda/src/main.rs index d0e415613..9db6b2750 100644 --- a/examples/http-basic-lambda/src/main.rs +++ b/examples/http-basic-lambda/src/main.rs @@ -3,7 +3,7 @@ use lambda_http::{run, service_fn, tracing, Body, Error, Request, Response}; /// This is the main body for the function. /// Write your code inside it. /// There are some code examples in the Runtime repository: -/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples +/// - async fn function_handler(_event: Request) -> Result, Error> { // Extract some useful information from the request diff --git a/examples/http-cors/Cargo.toml b/examples/http-cors/Cargo.toml index b8e510310..b9c9efa52 100644 --- a/examples/http-cors/Cargo.toml +++ b/examples/http-cors/Cargo.toml @@ -5,6 +5,5 @@ edition = "2021" [dependencies] lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } tokio = { version = "1", features = ["macros"] } -tower-http = { version = "0.5", features = ["cors"] } +tower-http = { version = "0.6", features = ["cors"] } diff --git a/examples/http-cors/src/main.rs b/examples/http-cors/src/main.rs index ffac0a9e8..4c2c54b4c 100644 --- a/examples/http-cors/src/main.rs +++ b/examples/http-cors/src/main.rs @@ -28,7 +28,7 @@ async fn func(event: Request) -> Result, Error> { .query_string_parameters_ref() .and_then(|params| params.first("first_name")) { - Some(first_name) => format!("Hello, {}!", first_name).into_response().await, + Some(first_name) => format!("Hello, {first_name}!").into_response().await, None => Response::builder() .status(400) .body("Empty first name".into()) diff --git a/examples/http-dynamodb/Cargo.toml b/examples/http-dynamodb/Cargo.toml index f2b8db98e..d347c346e 100644 --- a/examples/http-dynamodb/Cargo.toml +++ b/examples/http-dynamodb/Cargo.toml @@ -4,11 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] -simple-error = "0.3.0" -serde_json = "1.0.107" -serde = { version = "1.0.189", features = ["derive"] } -serde_dynamo = {version = "^4.2.7", features = ["aws-sdk-dynamodb+0_33"]} +serde_json = "1.0.140" +serde = { version = "1.0.219", features = ["derive"] } +serde_dynamo = { version = "4.2.14", features = ["aws-sdk-dynamodb+1"] } lambda_http = { path = "../../lambda-http" } -aws-sdk-dynamodb = "0.33.0" -aws-config = "0.56.1" -tokio = { version = "1.33.0", features = ["macros"] } +aws-sdk-dynamodb = "1.82.0" +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +tokio = { version = "1.46.1", features = ["macros"] } diff --git a/examples/http-dynamodb/src/main.rs b/examples/http-dynamodb/src/main.rs index e5cbb2a34..0d37693fa 100644 --- a/examples/http-dynamodb/src/main.rs +++ b/examples/http-dynamodb/src/main.rs @@ -1,21 +1,21 @@ use aws_sdk_dynamodb::Client; use lambda_http::{run, service_fn, tracing, Body, Error, Request, Response}; use serde::{Deserialize, Serialize}; -use serde_dynamo::to_attribute_value; +use serde_dynamo::to_item; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Item { - pub p_type: String, + pub account_type: String, pub age: String, pub username: String, - pub first: String, - pub last: String, + pub first_name: String, + pub last_name: String, } /// This is the main body for the function. /// Write your code inside it. /// You can see more examples in Runtime's repository: -/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples +/// - async fn handle_request(db_client: &Client, event: Request) -> Result, Error> { // Extract some useful information from the request let body = event.body(); @@ -71,20 +71,9 @@ async fn main() -> Result<(), Error> { // Add an item to a table. // snippet-start:[dynamodb.rust.add-item] pub async fn add_item(client: &Client, item: Item, table: &str) -> Result<(), Error> { - let user_av = to_attribute_value(item.username)?; - let type_av = to_attribute_value(item.p_type)?; - let age_av = to_attribute_value(item.age)?; - let first_av = to_attribute_value(item.first)?; - let last_av = to_attribute_value(item.last)?; + let item = to_item(item)?; - let request = client - .put_item() - .table_name(table) - .item("username", user_av) - .item("account_type", type_av) - .item("age", age_av) - .item("first_name", first_av) - .item("last_name", last_av); + let request = client.put_item().table_name(table).set_item(Some(item)); tracing::info!("adding item to DynamoDB"); diff --git a/examples/http-query-parameters/Cargo.toml b/examples/http-query-parameters/Cargo.toml index 2cadda95d..18f8e6cfb 100644 --- a/examples/http-query-parameters/Cargo.toml +++ b/examples/http-query-parameters/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-query-parameters/src/main.rs b/examples/http-query-parameters/src/main.rs index 1f7110a5b..c974e6330 100644 --- a/examples/http-query-parameters/src/main.rs +++ b/examples/http-query-parameters/src/main.rs @@ -3,7 +3,7 @@ use lambda_http::{run, service_fn, tracing, Error, IntoResponse, Request, Reques /// This is the main body for the function. /// Write your code inside it. /// You can see more examples in Runtime's repository: -/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples +/// - async fn function_handler(event: Request) -> Result { // Extract some useful information from the request Ok( @@ -11,7 +11,7 @@ async fn function_handler(event: Request) -> Result { .query_string_parameters_ref() .and_then(|params| params.first("first_name")) { - Some(first_name) => format!("Hello, {}!", first_name).into_response().await, + Some(first_name) => format!("Hello, {first_name}!").into_response().await, None => Response::builder() .status(400) .body("Empty first name".into()) diff --git a/examples/http-raw-path/Cargo.toml b/examples/http-raw-path/Cargo.toml index f6c56526d..d1c5ccb82 100644 --- a/examples/http-raw-path/Cargo.toml +++ b/examples/http-raw-path/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-shared-resource/Cargo.toml b/examples/http-shared-resource/Cargo.toml index 923ceecc1..8f5a0e947 100644 --- a/examples/http-shared-resource/Cargo.toml +++ b/examples/http-shared-resource/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] lambda_http = { path = "../../lambda-http" } -lambda_runtime = { path = "../../lambda-runtime" } tokio = { version = "1", features = ["macros"] } diff --git a/examples/http-tower-trace/Cargo.toml b/examples/http-tower-trace/Cargo.toml index 36389f0e5..cf1f223bb 100644 --- a/examples/http-tower-trace/Cargo.toml +++ b/examples/http-tower-trace/Cargo.toml @@ -5,6 +5,5 @@ edition = "2021" [dependencies] lambda_http = { path = "../../lambda-http" } -lambda_runtime = "0.5.1" tokio = { version = "1", features = ["macros"] } -tower-http = { version = "0.5", features = ["trace"] } +tower-http = { version = "0.6", features = ["trace"] } diff --git a/examples/http-tower-trace/src/main.rs b/examples/http-tower-trace/src/main.rs index df2b90073..7dd0c1d6a 100644 --- a/examples/http-tower-trace/src/main.rs +++ b/examples/http-tower-trace/src/main.rs @@ -1,6 +1,9 @@ -use lambda_http::tracing::{self, Level}; -use lambda_http::{run, tower::ServiceBuilder, Error}; -use lambda_http::{Request, Response}; +use lambda_http::{ + run, + tower::ServiceBuilder, + tracing::{self, Level}, + Error, Request, Response, +}; use tower_http::trace::{DefaultOnRequest, DefaultOnResponse, TraceLayer}; async fn handler(_req: Request) -> Result, Error> { diff --git a/examples/lambda-rds-iam-auth/Cargo.toml b/examples/lambda-rds-iam-auth/Cargo.toml index a1e212ae2..68b0fe929 100644 --- a/examples/lambda-rds-iam-auth/Cargo.toml +++ b/examples/lambda-rds-iam-auth/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [dependencies] lambda_runtime = { path = "../../lambda-runtime" } -serde_json = "1.0.120" -aws-config = "1.0.1" -aws-credential-types = "1.0.1" -aws-sigv4 = "1.0.1" -url = "2.5.0" -tokio = { version = "1.25.0", features = ["full"] } -sqlx = { version = "0.7.4", features = ["tls-rustls", "postgres", "runtime-tokio"] } +serde_json = "1.0.140" +aws-config = { version = "1.8.1", features = ["behavior-version-latest"] } +aws-credential-types = "1.2.3" +aws-sigv4 = "1.3.3" +url = "2.5.4" +tokio = { version = "1.46.1", features = ["full"] } +sqlx = { version = "0.8.6", features = ["tls-rustls", "postgres", "runtime-tokio"] } diff --git a/examples/lambda-rds-iam-auth/src/main.rs b/examples/lambda-rds-iam-auth/src/main.rs index 32cf35803..fbf8394f3 100644 --- a/examples/lambda-rds-iam-auth/src/main.rs +++ b/examples/lambda-rds-iam-auth/src/main.rs @@ -7,17 +7,15 @@ use aws_sigv4::{ use lambda_runtime::{run, service_fn, Error, LambdaEvent}; use serde_json::{json, Value}; use sqlx::postgres::PgConnectOptions; -use std::env; -use std::time::{Duration, SystemTime}; +use std::{ + env, + time::{Duration, SystemTime}, +}; const RDS_CERTS: &[u8] = include_bytes!("global-bundle.pem"); -async fn generate_rds_iam_token( - db_hostname: &str, - port: u16, - db_username: &str, -) -> Result { - let config = aws_config::load_defaults(BehaviorVersion::v2024_03_28()).await; +async fn generate_rds_iam_token(db_hostname: &str, port: u16, db_username: &str) -> Result { + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; let credentials = config .credentials_provider() @@ -40,23 +38,16 @@ async fn generate_rds_iam_token( .settings(signing_settings) .build()?; - let url = format!( - "https://{db_hostname}:{port}/?Action=connect&DBUser={db_user}", - db_hostname = db_hostname, - port = port, - db_user = db_username - ); + let url = format!("https://{db_hostname}:{port}/?Action=connect&DBUser={db_username}"); let signable_request = - SignableRequest::new("GET", &url, std::iter::empty(), SignableBody::Bytes(&[])) - .expect("signable request"); + SignableRequest::new("GET", &url, std::iter::empty(), SignableBody::Bytes(&[])).expect("signable request"); - let (signing_instructions, _signature) = - sign(signable_request, &signing_params.into())?.into_parts(); + let (signing_instructions, _signature) = sign(signable_request, &signing_params.into())?.into_parts(); let mut url = url::Url::parse(&url).unwrap(); for (name, value) in signing_instructions.params() { - url.query_pairs_mut().append_pair(name, &value); + url.query_pairs_mut().append_pair(name, value); } let response = url.to_string().split_off("https://".len()); @@ -89,9 +80,7 @@ async fn handler(_event: LambdaEvent) -> Result { .ssl_root_cert_from_pem(RDS_CERTS.to_vec()) .ssl_mode(sqlx::postgres::PgSslMode::Require); - let pool = sqlx::postgres::PgPoolOptions::new() - .connect_with(opts) - .await?; + let pool = sqlx::postgres::PgPoolOptions::new().connect_with(opts).await?; let result: i32 = sqlx::query_scalar("SELECT $1 + $2") .bind(3) @@ -99,7 +88,7 @@ async fn handler(_event: LambdaEvent) -> Result { .fetch_one(&pool) .await?; - println!("Result: {:?}", result); + println!("Result: {result:?}"); Ok(json!({ "statusCode": 200, diff --git a/examples/opentelemetry-tracing/Cargo.toml b/examples/opentelemetry-tracing/Cargo.toml index 98108d396..c9b2a9cb2 100644 --- a/examples/opentelemetry-tracing/Cargo.toml +++ b/examples/opentelemetry-tracing/Cargo.toml @@ -5,14 +5,12 @@ edition = "2021" [dependencies] lambda_runtime = { path = "../../lambda-runtime", features = ["opentelemetry"] } -opentelemetry-semantic-conventions = "0.14" -opentelemetry = "0.22" -opentelemetry_sdk = { version = "0.22", features = ["rt-tokio"] } -opentelemetry-stdout = { version = "0.3", features = ["trace"] } -pin-project = "1" +opentelemetry = "0.30" +opentelemetry_sdk = { version = "0.30", features = ["rt-tokio"] } +opentelemetry-stdout = { version = "0.30", features = ["trace"] } serde_json = "1.0" tokio = "1" tower = "0.5" tracing = "0.1" -tracing-opentelemetry = "0.23" +tracing-opentelemetry = "0.31" tracing-subscriber = "0.3" diff --git a/examples/opentelemetry-tracing/src/main.rs b/examples/opentelemetry-tracing/src/main.rs index 062f5a110..a75aa0168 100644 --- a/examples/opentelemetry-tracing/src/main.rs +++ b/examples/opentelemetry-tracing/src/main.rs @@ -4,7 +4,7 @@ use lambda_runtime::{ LambdaEvent, Runtime, }; use opentelemetry::trace::TracerProvider; -use opentelemetry_sdk::{runtime, trace}; +use opentelemetry_sdk::trace; use tower::{service_fn, BoxError}; use tracing_subscriber::prelude::*; @@ -18,8 +18,8 @@ async fn echo(event: LambdaEvent) -> Result Result<(), BoxError> { // Set up OpenTelemetry tracer provider that writes spans to stdout for debugging purposes let exporter = opentelemetry_stdout::SpanExporter::default(); - let tracer_provider = trace::TracerProvider::builder() - .with_batch_exporter(exporter, runtime::Tokio) + let tracer_provider = trace::SdkTracerProvider::builder() + .with_batch_exporter(exporter) .build(); // Set up link between OpenTelemetry and tracing crate @@ -34,7 +34,9 @@ async fn main() -> Result<(), BoxError> { // Create a tracing span for each Lambda invocation OtelLayer::new(|| { // Make sure that the trace is exported before the Lambda runtime is frozen - tracer_provider.force_flush(); + if let Err(err) = tracer_provider.force_flush() { + eprintln!("Error flushing traces: {err:#?}"); + } }) // Set the "faas.trigger" attribute of the span to "pubsub" .with_trigger(OpenTelemetryFaasTrigger::PubSub), diff --git a/lambda-events/Cargo.toml b/lambda-events/Cargo.toml index b8d34055e..c21e959b4 100644 --- a/lambda-events/Cargo.toml +++ b/lambda-events/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "aws_lambda_events" -version = "0.16.0" +version = "0.18.0" +rust-version = "1.82.0" description = "AWS Lambda event definitions" authors = [ "Christian Legnitto ", @@ -123,3 +124,8 @@ sqs = ["serde_with"] streams = [] documentdb = [] eventbridge = ["chrono", "serde_with"] + +catch-all-fields = [] + +[package.metadata.docs.rs] +all-features = true diff --git a/lambda-events/src/custom_serde/codebuild_time.rs b/lambda-events/src/custom_serde/codebuild_time.rs index 92b0f7961..8d90203fc 100644 --- a/lambda-events/src/custom_serde/codebuild_time.rs +++ b/lambda-events/src/custom_serde/codebuild_time.rs @@ -14,13 +14,13 @@ impl Visitor<'_> for TimeVisitor { type Value = DateTime; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "valid codebuild time: {}", CODEBUILD_TIME_FORMAT) + write!(formatter, "valid codebuild time: {CODEBUILD_TIME_FORMAT}") } fn visit_str(self, val: &str) -> Result { NaiveDateTime::parse_from_str(val, CODEBUILD_TIME_FORMAT) .map(|naive| naive.and_utc()) - .map_err(|e| DeError::custom(format!("Parse error {} for {}", e, val))) + .map_err(|e| DeError::custom(format!("Parse error {e} for {val}"))) } } diff --git a/lambda-events/src/custom_serde/float_unix_epoch.rs b/lambda-events/src/custom_serde/float_unix_epoch.rs index 805c672f3..164b4c7e6 100644 --- a/lambda-events/src/custom_serde/float_unix_epoch.rs +++ b/lambda-events/src/custom_serde/float_unix_epoch.rs @@ -14,7 +14,7 @@ fn ne_timestamp(ts: T) -> SerdeError { impl fmt::Debug for SerdeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ChronoSerdeError({})", self) + write!(f, "ChronoSerdeError({self})") } } @@ -22,7 +22,7 @@ impl fmt::Display for SerdeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { SerdeError::NonExistent { ref timestamp } => { - write!(f, "value is not a legal timestamp: {}", timestamp) + write!(f, "value is not a legal timestamp: {timestamp}") } SerdeError::Ambiguous { ref timestamp, @@ -30,8 +30,7 @@ impl fmt::Display for SerdeError { ref max, } => write!( f, - "value is an ambiguous timestamp: {}, could be either of {}, {}", - timestamp, min, max + "value is an ambiguous timestamp: {timestamp}, could be either of {min}, {max}", ), } } @@ -57,7 +56,6 @@ where struct SecondsFloatTimestampVisitor; /// Serialize a UTC datetime into an float number of seconds since the epoch -/// ``` pub fn serialize(dt: &DateTime, serializer: S) -> Result where S: ser::Serializer, diff --git a/lambda-events/src/custom_serde/headers.rs b/lambda-events/src/custom_serde/headers.rs index 448846498..ea52c88e7 100644 --- a/lambda-events/src/custom_serde/headers.rs +++ b/lambda-events/src/custom_serde/headers.rs @@ -5,7 +5,7 @@ use serde::{ }; use std::{borrow::Cow, fmt}; -/// Serialize a http::HeaderMap into a serde str => Vec map +/// Serialize a http::HeaderMap into a serde str => `Vec` map pub(crate) fn serialize_multi_value_headers(headers: &HeaderMap, serializer: S) -> Result where S: Serializer, diff --git a/lambda-events/src/custom_serde/mod.rs b/lambda-events/src/custom_serde/mod.rs index 729dee3dd..02b50c783 100644 --- a/lambda-events/src/custom_serde/mod.rs +++ b/lambda-events/src/custom_serde/mod.rs @@ -8,6 +8,7 @@ use std::collections::HashMap; #[cfg(feature = "codebuild")] pub(crate) mod codebuild_time; #[cfg(feature = "codebuild")] +#[cfg_attr(docsrs, doc(cfg(feature = "codebuild")))] pub type CodeBuildNumber = f32; #[cfg(any( @@ -33,6 +34,11 @@ pub(crate) mod float_unix_epoch; #[cfg(any(feature = "alb", feature = "apigw"))] pub(crate) mod http_method; +#[cfg(feature = "alb")] +mod query_string_parameters; +#[cfg(feature = "alb")] +pub(crate) use self::query_string_parameters::*; + pub(crate) fn deserialize_base64<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, diff --git a/lambda-events/src/custom_serde/query_string_parameters.rs b/lambda-events/src/custom_serde/query_string_parameters.rs new file mode 100644 index 000000000..a7144edab --- /dev/null +++ b/lambda-events/src/custom_serde/query_string_parameters.rs @@ -0,0 +1,63 @@ +use query_map::QueryMap; +use serde::{ser::SerializeMap, Serializer}; +use std::collections::HashMap; + +/// Serializes `QueryMap`, converting value from `Vec` to `String` using the last value. +pub fn serialize_query_string_parameters(value: &QueryMap, serializer: S) -> Result +where + S: Serializer, +{ + let mut query_string_parameters = HashMap::new(); + + if let Some((mut last_key, mut last_value)) = value.iter().next() { + // insert the last value for each key + value.iter().for_each(|(k, v)| { + if k != last_key { + query_string_parameters.insert(last_key, last_value); + last_key = k; + } + last_value = v; + }); + // insert the last pair + query_string_parameters.insert(last_key, last_value); + } + + let mut map = serializer.serialize_map(Some(query_string_parameters.len()))?; + for (k, v) in &query_string_parameters { + map.serialize_entry(k, v)?; + } + map.end() +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Serialize; + use serde_json::Value; + + #[test] + fn test_serialize_query_string_parameters() { + #[derive(Serialize)] + struct Test { + #[serde(serialize_with = "serialize_query_string_parameters")] + pub v: QueryMap, + } + + fn s(value: &str) -> String { + value.to_string() + } + + let query = QueryMap::from(HashMap::from([ + (s("key1"), vec![s("value1"), s("value2"), s("value3")]), + (s("key2"), vec![s("value4")]), + (s("key3"), vec![s("value5"), s("value6")]), + ])); + + let serialized = serde_json::to_string(&Test { v: query }).unwrap(); + let parsed: Value = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(parsed["v"]["key1"], Value::String("value3".to_string())); + assert_eq!(parsed["v"]["key2"], Value::String("value4".to_string())); + assert_eq!(parsed["v"]["key3"], Value::String("value6".to_string())); + } +} diff --git a/lambda-events/src/encodings/http.rs b/lambda-events/src/encodings/http.rs index d978f522a..32137a759 100644 --- a/lambda-events/src/encodings/http.rs +++ b/lambda-events/src/encodings/http.rs @@ -60,6 +60,7 @@ use std::{borrow::Cow, mem::take, ops::Deref, pin::Pin, task::Poll}; /// /// For more information about API Gateway's body types, /// refer to [this documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html). +#[non_exhaustive] #[derive(Debug, Default, Eq, PartialEq)] pub enum Body { /// An empty body @@ -264,7 +265,7 @@ mod tests { fn from_str() { match Body::from(String::from("foo").as_str()) { Body::Text(_) => (), - not => panic!("expected Body::Text(...) got {:?}", not), + not => panic!("expected Body::Text(...) got {not:?}"), } } @@ -272,7 +273,7 @@ mod tests { fn from_string() { match Body::from(String::from("foo")) { Body::Text(_) => (), - not => panic!("expected Body::Text(...) got {:?}", not), + not => panic!("expected Body::Text(...) got {not:?}"), } } @@ -280,7 +281,7 @@ mod tests { fn from_cow_str() { match Body::from(Cow::from("foo")) { Body::Text(_) => (), - not => panic!("expected Body::Text(...) got {:?}", not), + not => panic!("expected Body::Text(...) got {not:?}"), } } @@ -288,7 +289,7 @@ mod tests { fn from_cow_bytes() { match Body::from(Cow::from("foo".as_bytes())) { Body::Binary(_) => (), - not => panic!("expected Body::Binary(...) got {:?}", not), + not => panic!("expected Body::Binary(...) got {not:?}"), } } @@ -296,7 +297,7 @@ mod tests { fn from_bytes() { match Body::from("foo".as_bytes()) { Body::Binary(_) => (), - not => panic!("expected Body::Binary(...) got {:?}", not), + not => panic!("expected Body::Binary(...) got {not:?}"), } } @@ -325,12 +326,12 @@ mod tests { fn serialize_from_maybe_encoded() { match Body::from_maybe_encoded(false, "foo") { Body::Text(_) => (), - not => panic!("expected Body::Text(...) got {:?}", not), + not => panic!("expected Body::Text(...) got {not:?}"), } match Body::from_maybe_encoded(true, "Zm9v") { Body::Binary(b) => assert_eq!(&[102, 111, 111], b.as_slice()), - not => panic!("expected Body::Text(...) got {:?}", not), + not => panic!("expected Body::Text(...) got {not:?}"), } } } diff --git a/lambda-events/src/encodings/mod.rs b/lambda-events/src/encodings/mod.rs index 233996648..8b3089477 100644 --- a/lambda-events/src/encodings/mod.rs +++ b/lambda-events/src/encodings/mod.rs @@ -6,15 +6,18 @@ mod time; use crate::custom_serde::{deserialize_base64, serialize_base64}; #[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub use self::time::*; #[cfg(feature = "http")] mod http; #[cfg(feature = "http")] +#[cfg_attr(docsrs, doc(cfg(feature = "http")))] pub use self::http::*; pub type Error = Box; /// Binary data encoded in base64. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct Base64Data( #[serde(deserialize_with = "deserialize_base64")] diff --git a/lambda-events/src/encodings/time.rs b/lambda-events/src/encodings/time.rs index d49033608..4e6d802b8 100644 --- a/lambda-events/src/encodings/time.rs +++ b/lambda-events/src/encodings/time.rs @@ -7,7 +7,8 @@ use serde::{ use std::ops::{Deref, DerefMut}; /// Timestamp with millisecond precision. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct MillisecondTimestamp( #[serde(deserialize_with = "deserialize_milliseconds")] #[serde(serialize_with = "serialize_milliseconds")] @@ -29,7 +30,8 @@ impl DerefMut for MillisecondTimestamp { } /// Timestamp with second precision. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct SecondTimestamp( #[serde(deserialize_with = "deserialize_seconds")] #[serde(serialize_with = "serialize_seconds")] @@ -51,6 +53,7 @@ impl DerefMut for SecondTimestamp { } /// Duration with second precision. +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct SecondDuration( #[serde(deserialize_with = "deserialize_duration_seconds")] @@ -73,7 +76,8 @@ impl DerefMut for SecondDuration { } /// Duration with minute precision. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct MinuteDuration( #[serde(deserialize_with = "deserialize_duration_minutes")] #[serde(serialize_with = "serialize_duration_minutes")] @@ -125,7 +129,7 @@ where let whole_seconds = seconds + (milliseconds as i64 / 1000); let subsec_millis = milliseconds % 1000; if milliseconds > 0 { - let combined = format!("{}.{:03}", whole_seconds, subsec_millis); + let combined = format!("{whole_seconds}.{subsec_millis:03}"); serializer.serialize_str(&combined) } else { serializer.serialize_str(&whole_seconds.to_string()) @@ -159,7 +163,7 @@ where { let seconds = f64::deserialize(deserializer)?; TimeDelta::try_seconds(seconds as i64) - .ok_or_else(|| D::Error::custom(format!("invalid time delta seconds `{}`", seconds))) + .ok_or_else(|| D::Error::custom(format!("invalid time delta seconds `{seconds}`"))) } fn serialize_duration_minutes(duration: &TimeDelta, serializer: S) -> Result @@ -177,7 +181,7 @@ where { let minutes = f64::deserialize(deserializer)?; TimeDelta::try_minutes(minutes as i64) - .ok_or_else(|| D::Error::custom(format!("invalid time delta minutes `{}`", minutes))) + .ok_or_else(|| D::Error::custom(format!("invalid time delta minutes `{minutes}`"))) } fn normalize_timestamp<'de, D>(deserializer: D) -> Result<(u64, u64), D::Error> @@ -199,7 +203,7 @@ where }; // We need to do this due to floating point issues. - let input_as_string = format!("{}", input); + let input_as_string = input.to_string(); let parts: Result, _> = input_as_string .split('.') .map(|x| x.parse::().map_err(DeError::custom)) diff --git a/lambda-events/src/event/activemq/mod.rs b/lambda-events/src/event/activemq/mod.rs index 89cfda1c2..60ef85687 100644 --- a/lambda-events/src/event/activemq/mod.rs +++ b/lambda-events/src/event/activemq/mod.rs @@ -1,9 +1,12 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ActiveMqEvent { #[serde(default)] @@ -11,9 +14,17 @@ pub struct ActiveMqEvent { #[serde(default)] pub event_source_arn: Option, pub messages: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ActiveMqMessage { #[serde(default)] @@ -41,13 +52,28 @@ pub struct ActiveMqMessage { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub properties: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ActiveMqDestination { #[serde(default)] pub physical_name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/alb/mod.rs b/lambda-events/src/event/alb/mod.rs index 3b4ce9d5e..1829bf014 100644 --- a/lambda-events/src/event/alb/mod.rs +++ b/lambda-events/src/event/alb/mod.rs @@ -1,14 +1,18 @@ use crate::{ custom_serde::{ - deserialize_headers, deserialize_nullish_boolean, http_method, serialize_headers, serialize_multi_value_headers, + deserialize_headers, deserialize_nullish_boolean, http_method, serialize_headers, + serialize_multi_value_headers, serialize_query_string_parameters, }, encodings::Body, }; use http::{HeaderMap, Method}; use query_map::QueryMap; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `AlbTargetGroupRequest` contains data originating from the ALB Lambda target group integration +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AlbTargetGroupRequest { @@ -17,6 +21,7 @@ pub struct AlbTargetGroupRequest { #[serde(default)] pub path: Option, #[serde(default)] + #[serde(serialize_with = "serialize_query_string_parameters")] pub query_string_parameters: QueryMap, #[serde(default)] pub multi_value_query_string_parameters: QueryMap, @@ -30,25 +35,49 @@ pub struct AlbTargetGroupRequest { #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub is_base64_encoded: bool, pub body: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AlbTargetGroupRequestContext` contains the information to identify the load balancer invoking the lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AlbTargetGroupRequestContext { pub elb: ElbContext, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ElbContext` contains the information to identify the ARN invoking the lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ElbContext { /// nolint: stylecheck #[serde(default)] pub target_group_arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AlbTargetGroupResponse` configures the response to be returned by the ALB Lambda target group for the request +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AlbTargetGroupResponse { @@ -65,11 +94,19 @@ pub struct AlbTargetGroupResponse { pub body: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub is_base64_encoded: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] mod test { use super::*; + use serde_json::Value; #[test] #[cfg(feature = "alb")] @@ -91,6 +128,19 @@ mod test { assert_eq!(parsed, reparsed); } + #[test] + #[cfg(feature = "alb")] + fn ensure_alb_lambda_target_request_query_string_parameter_value_is_string() { + let data = include_bytes!("../../fixtures/example-alb-lambda-target-request-headers-only.json"); + let parsed: AlbTargetGroupRequest = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: Value = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!( + reparsed["queryStringParameters"]["key"], + Value::String("hello".to_string()) + ); + } + #[test] #[cfg(feature = "alb")] fn example_alb_lambda_target_response() { diff --git a/lambda-events/src/event/apigw/mod.rs b/lambda-events/src/event/apigw/mod.rs index e0aa1e8c1..015eff402 100644 --- a/lambda-events/src/event/apigw/mod.rs +++ b/lambda-events/src/event/apigw/mod.rs @@ -13,6 +13,7 @@ use serde_json::Value; use std::collections::HashMap; /// `ApiGatewayProxyRequest` contains data coming from the API Gateway proxy +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayProxyRequest { @@ -47,9 +48,17 @@ pub struct ApiGatewayProxyRequest { pub body: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub is_base64_encoded: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayProxyResponse` configures the response to be returned by API Gateway for the request +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayProxyResponse { @@ -64,10 +73,18 @@ pub struct ApiGatewayProxyResponse { pub body: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub is_base64_encoded: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayProxyRequestContext` contains the information to identify the AWS account and resources invoking the /// Lambda function. It also includes Cognito identity information for the caller. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayProxyRequestContext { @@ -110,9 +127,17 @@ pub struct ApiGatewayProxyRequestContext { #[serde(default)] #[serde(rename = "apiId")] pub apiid: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpRequest` contains data coming from the new HTTP API Gateway +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpRequest { @@ -160,9 +185,17 @@ pub struct ApiGatewayV2httpRequest { pub body: Option, #[serde(default)] pub is_base64_encoded: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpRequestContext` contains the information to identify the AWS account and resources invoking the Lambda function. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpRequestContext { @@ -192,9 +225,17 @@ pub struct ApiGatewayV2httpRequestContext { pub http: ApiGatewayV2httpRequestContextHttpDescription, #[serde(skip_serializing_if = "Option::is_none")] pub authentication: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayRequestAuthorizer` contains authorizer information for the request context. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct ApiGatewayRequestAuthorizer { #[serde(skip_serializing_if = "Option::is_none")] @@ -209,9 +250,17 @@ pub struct ApiGatewayRequestAuthorizer { pub fields: HashMap, #[serde(skip_serializing_if = "Option::is_none")] pub iam: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayRequestAuthorizerJwtDescription` contains JWT authorizer information for the request context. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayRequestAuthorizerJwtDescription { @@ -220,9 +269,17 @@ pub struct ApiGatewayRequestAuthorizerJwtDescription { pub claims: HashMap, #[serde(skip_serializing_if = "Option::is_none")] pub scopes: Option>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayRequestAuthorizerIamDescription` contains IAM information for the request context. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayRequestAuthorizerIamDescription { @@ -240,9 +297,17 @@ pub struct ApiGatewayRequestAuthorizerIamDescription { pub user_arn: Option, #[serde(default)] pub user_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayRequestAuthorizerCognitoIdentity` contains Cognito identity information for the request context. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayRequestAuthorizerCognitoIdentity { @@ -251,9 +316,17 @@ pub struct ApiGatewayRequestAuthorizerCognitoIdentity { pub identity_id: Option, #[serde(default)] pub identity_pool_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpRequestContextHttpDescription` contains HTTP information for the request context. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpRequestContextHttpDescription { @@ -267,9 +340,17 @@ pub struct ApiGatewayV2httpRequestContextHttpDescription { pub source_ip: Option, #[serde(default)] pub user_agent: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpResponse` configures the response to be returned by API Gateway V2 for the request +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpResponse { @@ -285,9 +366,17 @@ pub struct ApiGatewayV2httpResponse { #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub is_base64_encoded: bool, pub cookies: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayRequestIdentity` contains identity information for the request caller. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayRequestIdentity { @@ -318,9 +407,17 @@ pub struct ApiGatewayRequestIdentity { pub user_agent: Option, #[serde(default)] pub user: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayWebsocketProxyRequest` contains data coming from the API Gateway proxy +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayWebsocketProxyRequest { @@ -357,11 +454,19 @@ pub struct ApiGatewayWebsocketProxyRequest { pub body: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub is_base64_encoded: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayWebsocketProxyRequestContext` contains the information to identify /// the AWS account and resources invoking the Lambda function. It also includes /// Cognito identity information for the caller. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayWebsocketProxyRequestContext { @@ -421,9 +526,17 @@ pub struct ApiGatewayWebsocketProxyRequestContext { pub disconnect_status_code: Option, #[serde(default)] pub disconnect_reason: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerRequestTypeRequestIdentity` contains identity information for the request caller including certificate information if using mTLS. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerRequestTypeRequestIdentity { @@ -435,9 +548,17 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequestIdentity { pub source_ip: Option, #[serde(default)] pub client_cert: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCert` contains certificate information for the request caller if using mTLS. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCert { @@ -452,9 +573,17 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCert { #[serde(rename = "subjectDN")] pub subject_dn: Option, pub validity: ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCertValidity, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCertValidity` contains certificate validity information for the request caller if using mTLS. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCertValidity { @@ -462,17 +591,33 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequestIdentityClientCertValidit pub not_after: Option, #[serde(default)] pub not_before: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpRequestContextAuthentication` contains authentication context information for the request caller including client certificate information if using mTLS. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpRequestContextAuthentication { #[serde(default)] pub client_cert: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpRequestContextAuthenticationClientCert` contains client certificate information for the request caller if using mTLS. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpRequestContextAuthenticationClientCert { @@ -487,9 +632,17 @@ pub struct ApiGatewayV2httpRequestContextAuthenticationClientCert { #[serde(rename = "subjectDN")] pub subject_dn: Option, pub validity: ApiGatewayV2httpRequestContextAuthenticationClientCertValidity, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2httpRequestContextAuthenticationClientCertValidity` contains client certificate validity information for the request caller if using mTLS. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2httpRequestContextAuthenticationClientCertValidity { @@ -497,8 +650,16 @@ pub struct ApiGatewayV2httpRequestContextAuthenticationClientCertValidity { pub not_after: Option, #[serde(default)] pub not_before: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2CustomAuthorizerV1RequestTypeRequestContext { @@ -520,8 +681,16 @@ pub struct ApiGatewayV2CustomAuthorizerV1RequestTypeRequestContext { #[serde(default)] #[serde(rename = "apiId")] pub apiid: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2CustomAuthorizerV1Request { @@ -555,8 +724,16 @@ pub struct ApiGatewayV2CustomAuthorizerV1Request { #[serde(default)] pub stage_variables: HashMap, pub request_context: ApiGatewayV2CustomAuthorizerV1RequestTypeRequestContext, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2CustomAuthorizerV2Request { @@ -590,10 +767,18 @@ pub struct ApiGatewayV2CustomAuthorizerV2Request { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub stage_variables: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerContext` represents the expected format of an API Gateway custom authorizer response. /// Deprecated. Code should be updated to use the Authorizer map from APIGatewayRequestIdentity. Ex: Authorizer["principalId"] +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerContext { @@ -602,9 +787,17 @@ pub struct ApiGatewayCustomAuthorizerContext { pub num_key: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub bool_key: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerRequestTypeRequestContext` represents the expected format of an API Gateway custom authorizer response. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerRequestTypeRequestContext { @@ -630,9 +823,17 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequestContext { #[serde(default)] #[serde(rename = "apiId")] pub apiid: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerRequest` contains data coming in to a custom API Gateway authorizer function. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerRequest { @@ -643,9 +844,17 @@ pub struct ApiGatewayCustomAuthorizerRequest { /// nolint: stylecheck #[serde(default)] pub method_arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerRequestTypeRequest` contains data coming in to a custom API Gateway authorizer function. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerRequestTypeRequest { @@ -680,9 +889,17 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequest { #[serde(default)] pub stage_variables: HashMap, pub request_context: ApiGatewayCustomAuthorizerRequestTypeRequestContext, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerResponse` represents the expected format of an API Gateway authorization response. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayCustomAuthorizerResponse @@ -696,9 +913,17 @@ where #[serde(bound = "", default)] pub context: T1, pub usage_identifier_key: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayV2CustomAuthorizerSimpleResponse` represents the simple format of an API Gateway V2 authorization response. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2CustomAuthorizerSimpleResponse @@ -709,8 +934,16 @@ where pub is_authorized: bool, #[serde(bound = "", default)] pub context: T1, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2CustomAuthorizerIamPolicyResponse @@ -723,15 +956,30 @@ where pub policy_document: ApiGatewayCustomAuthorizerPolicy, #[serde(bound = "", default)] pub context: T1, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ApiGatewayCustomAuthorizerPolicy` represents an IAM policy +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct ApiGatewayCustomAuthorizerPolicy { #[serde(default)] pub version: Option, pub statement: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } fn default_http_method() -> Method { @@ -876,6 +1124,24 @@ mod test { assert!(output.contains(r#""headername":"headerValue2""#)); } + #[test] + #[cfg(all(feature = "apigw", feature = "catch-all-fields"))] + fn example_apigw_request_catch_all() { + use serde_json::json; + + let data = include_bytes!("../../fixtures/example-apigw-request-catch-all.json"); + let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + + assert_eq!(parsed.other.get("otherField"), Some(&json!("foobar"))); + assert_eq!( + parsed.request_context.identity.other.get("otherField"), + Some(&json!(2345)) + ); + } + #[test] #[cfg(feature = "apigw")] fn example_apigw_restapi_openapi_request() { diff --git a/lambda-events/src/event/appsync/mod.rs b/lambda-events/src/event/appsync/mod.rs index 63f9ac74b..223c706bf 100644 --- a/lambda-events/src/event/appsync/mod.rs +++ b/lambda-events/src/event/appsync/mod.rs @@ -4,8 +4,9 @@ use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; -/// Deprecated: `AppSyncResolverTemplate` does not represent resolver events sent by AppSync. Instead directly model your input schema, or use map[string]string, json.RawMessage, interface{}, etc.. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +/// Deprecated: `AppSyncResolverTemplate` does not represent resolver events sent by AppSync. Instead directly model your input schema, or use `map[string]string`, `json.RawMessage`,` interface{}`, etc.. +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AppSyncResolverTemplate where @@ -17,10 +18,18 @@ where pub operation: AppSyncOperation, #[serde(bound = "")] pub payload: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AppSyncIamIdentity` contains information about the caller authed via IAM. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AppSyncIamIdentity { #[serde(default)] @@ -38,10 +47,18 @@ pub struct AppSyncIamIdentity { pub username: Option, #[serde(default)] pub user_arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AppSyncCognitoIdentity` contains information about the caller authed via Cognito. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AppSyncCognitoIdentity where @@ -61,22 +78,38 @@ where pub source_ip: Vec, #[serde(default)] pub default_auth_strategy: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type AppSyncOperation = String; /// `AppSyncLambdaAuthorizerRequest` contains an authorization request from AppSync. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AppSyncLambdaAuthorizerRequest { #[serde(default)] pub authorization_token: Option, pub request_context: AppSyncLambdaAuthorizerRequestContext, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AppSyncLambdaAuthorizerRequestContext` contains the parameters of the AppSync invocation which triggered /// this authorization request. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AppSyncLambdaAuthorizerRequestContext where @@ -98,10 +131,18 @@ where #[serde(default)] #[serde(bound = "")] pub variables: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AppSyncLambdaAuthorizerResponse` represents the expected format of an authorization response to AppSync. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AppSyncLambdaAuthorizerResponse where @@ -115,6 +156,174 @@ where pub resolver_context: HashMap, pub denied_fields: Option>, pub ttl_override: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} + +/// `AppSyncDirectResolverEvent` represents the default payload structure sent by AWS AppSync +/// when using **Direct Lambda Resolvers** (i.e., when both request and response mapping +/// templates are disabled). +/// +/// This structure includes the full AppSync **Context object**, as described in the +/// [AppSync Direct Lambda resolver reference](https://docs.aws.amazon.com/appsync/latest/devguide/direct-lambda-reference.html). +/// +/// It is recommended when working without VTL templates and relying on the standard +/// AppSync-to-Lambda event format. +/// +/// See also: +/// - [AppSync resolver mapping template context reference](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html) +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] +pub struct AppSyncDirectResolverEvent +where + TArguments: Serialize + DeserializeOwned, + TSource: Serialize + DeserializeOwned, + TStash: Serialize + DeserializeOwned, +{ + #[serde(bound = "")] + pub arguments: Option, + pub identity: Option, + #[serde(bound = "")] + pub source: Option, + pub request: AppSyncRequest, + pub info: AppSyncInfo, + #[serde(default)] + pub prev: Option, + #[serde(bound = "")] + pub stash: TStash, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} + +/// `AppSyncRequest` contains request-related metadata for a resolver invocation, +/// including client-sent headers and optional custom domain name. +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncRequest { + #[serde(deserialize_with = "deserialize_lambda_map")] + #[serde(default)] + #[serde(bound = "")] + pub headers: HashMap>, + #[serde(default)] + pub domain_name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} + +/// `AppSyncInfo` contains metadata about the current GraphQL field being resolved. +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncInfo +where + T: Serialize + DeserializeOwned, +{ + #[serde(default)] + pub selection_set_list: Vec, + #[serde(rename = "selectionSetGraphQL")] + pub selection_set_graphql: String, + pub parent_type_name: String, + pub field_name: String, + #[serde(bound = "")] + pub variables: T, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} + +/// `AppSyncPrevResult` contains the result of the previous step in a pipeline resolver. +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] +pub struct AppSyncPrevResult +where + T: Serialize + DeserializeOwned, +{ + #[serde(bound = "")] + pub result: T, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} + +/// `AppSyncIdentity` represents the identity of the caller as determined by the +/// configured AppSync authorization mechanism (IAM, Cognito, OIDC, or Lambda). +#[non_exhaustive] +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(untagged, rename_all = "camelCase")] +pub enum AppSyncIdentity { + IAM(AppSyncIamIdentity), + Cognito(AppSyncCognitoIdentity), + OIDC(AppSyncIdentityOIDC), + Lambda(AppSyncIdentityLambda), +} + +impl Default for AppSyncIdentity { + fn default() -> Self { + AppSyncIdentity::IAM(AppSyncIamIdentity::default()) + } +} + +/// `AppSyncIdentityOIDC` represents identity information when using OIDC-based authorization. +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] +pub struct AppSyncIdentityOIDC +where + T: Serialize + DeserializeOwned, +{ + #[serde(bound = "")] + pub claims: T, + pub issuer: String, + pub sub: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} + +/// `AppSyncIdentityLambda` represents identity information when using AWS Lambda +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncIdentityLambda +where + T: Serialize + DeserializeOwned, +{ + #[serde(bound = "")] + pub resolver_context: T, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] @@ -160,4 +369,14 @@ mod test { let reparsed: AppSyncLambdaAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); assert_eq!(parsed, reparsed); } + + #[test] + #[cfg(feature = "appsync")] + fn example_appsync_direct_resolver() { + let data = include_bytes!("../../fixtures/example-appsync-direct-resolver.json"); + let parsed: AppSyncDirectResolverEvent = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: AppSyncDirectResolverEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + } } diff --git a/lambda-events/src/event/autoscaling/mod.rs b/lambda-events/src/event/autoscaling/mod.rs index 601e87745..9a0eda8ad 100644 --- a/lambda-events/src/event/autoscaling/mod.rs +++ b/lambda-events/src/event/autoscaling/mod.rs @@ -6,7 +6,8 @@ use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; /// `AutoScalingEvent` struct is used to parse the json for auto scaling event types // -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct AutoScalingEvent where @@ -41,6 +42,13 @@ where #[serde(default)] #[serde(bound = "")] pub detail: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/bedrock_agent_runtime/mod.rs b/lambda-events/src/event/bedrock_agent_runtime/mod.rs index c1425b859..ce1199149 100644 --- a/lambda-events/src/event/bedrock_agent_runtime/mod.rs +++ b/lambda-events/src/event/bedrock_agent_runtime/mod.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; /// The Event sent to Lambda from Agents for Amazon Bedrock. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AgentEvent { @@ -29,22 +32,46 @@ pub struct AgentEvent { pub session_attributes: HashMap, /// Contains prompt attributes and their values. pub prompt_session_attributes: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct RequestBody { /// Contains the request body and its properties pub content: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Content { /// The content of the request body pub properties: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Property { @@ -54,8 +81,16 @@ pub struct Property { pub r#type: String, /// The value of the parameter pub value: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Parameter { @@ -65,8 +100,16 @@ pub struct Parameter { pub r#type: String, /// The value of the parameter pub value: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Agent { @@ -78,6 +121,13 @@ pub struct Agent { pub alias: String, /// The version of the agent. pub version: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/chime_bot/mod.rs b/lambda-events/src/event/chime_bot/mod.rs index 6581ed2c8..7a0990ef6 100644 --- a/lambda-events/src/event/chime_bot/mod.rs +++ b/lambda-events/src/event/chime_bot/mod.rs @@ -1,7 +1,10 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChimeBotEvent { #[serde(rename = "Sender")] @@ -17,9 +20,17 @@ pub struct ChimeBotEvent { pub event_timestamp: DateTime, #[serde(rename = "Message")] pub message: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChimeBotEventSender { #[serde(default)] @@ -28,9 +39,17 @@ pub struct ChimeBotEventSender { #[serde(default)] #[serde(rename = "SenderIdType")] pub sender_id_type: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChimeBotEventDiscussion { #[serde(default)] @@ -39,9 +58,17 @@ pub struct ChimeBotEventDiscussion { #[serde(default)] #[serde(rename = "DiscussionType")] pub discussion_type: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChimeBotEventInboundHttpsEndpoint { #[serde(default)] @@ -50,4 +77,11 @@ pub struct ChimeBotEventInboundHttpsEndpoint { #[serde(default)] #[serde(rename = "Url")] pub url: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/clientvpn/mod.rs b/lambda-events/src/event/clientvpn/mod.rs index 163abe720..3d6152c9c 100644 --- a/lambda-events/src/event/clientvpn/mod.rs +++ b/lambda-events/src/event/clientvpn/mod.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ClientVpnConnectionHandlerRequest { #[serde(default)] @@ -29,9 +32,17 @@ pub struct ClientVpnConnectionHandlerRequest { #[serde(default)] #[serde(rename = "schema-version")] pub schema_version: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ClientVpnConnectionHandlerResponse { pub allow: bool, @@ -43,6 +54,13 @@ pub struct ClientVpnConnectionHandlerResponse { #[serde(default)] #[serde(rename = "schema-version")] pub schema_version: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/cloudformation/mod.rs b/lambda-events/src/event/cloudformation/mod.rs index 44156b97a..3ea66e308 100644 --- a/lambda-events/src/event/cloudformation/mod.rs +++ b/lambda-events/src/event/cloudformation/mod.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; pub mod provider; +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "RequestType")] pub enum CloudFormationCustomResourceRequest @@ -19,7 +20,14 @@ where Delete(DeleteRequest), } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +impl Default for CloudFormationCustomResourceRequest { + fn default() -> Self { + CloudFormationCustomResourceRequest::Create(CreateRequest::default()) + } +} + +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CreateRequest where @@ -35,9 +43,17 @@ where pub logical_resource_id: String, #[serde(bound = "")] pub resource_properties: P2, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct UpdateRequest where @@ -57,9 +73,17 @@ where pub resource_properties: P2, #[serde(bound = "")] pub old_resource_properties: P1, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct DeleteRequest where @@ -76,9 +100,17 @@ where pub physical_resource_id: String, #[serde(bound = "")] pub resource_properties: P2, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudFormationCustomResourceResponse { pub status: CloudFormationCustomResourceResponseStatus, @@ -89,11 +121,20 @@ pub struct CloudFormationCustomResourceResponse { pub logical_resource_id: String, pub no_echo: bool, pub data: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum CloudFormationCustomResourceResponseStatus { + #[default] Success, Failed, } diff --git a/lambda-events/src/event/cloudformation/provider.rs b/lambda-events/src/event/cloudformation/provider.rs index a1594eb40..34e8136f4 100644 --- a/lambda-events/src/event/cloudformation/provider.rs +++ b/lambda-events/src/event/cloudformation/provider.rs @@ -2,11 +2,12 @@ //! //! Note that they are similar (but not the same) as the events in the `super` module. //! -//! See https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources-readme.html for details. +//! See for details. use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "RequestType")] pub enum CloudFormationCustomResourceRequest @@ -22,7 +23,14 @@ where Delete(DeleteRequest), } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +impl Default for CloudFormationCustomResourceRequest { + fn default() -> Self { + CloudFormationCustomResourceRequest::Create(CreateRequest::default()) + } +} + +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CreateRequest where @@ -30,9 +38,11 @@ where { #[serde(flatten, bound = "")] pub common: CommonRequestParams, + // No `other` catch-all here; any additional fields will be caught in `common.other` instead } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct UpdateRequest where @@ -46,9 +56,11 @@ where #[serde(flatten, bound = "")] pub common: CommonRequestParams, + // No `other` catch-all here; any additional fields will be caught in `common.other` instead } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct DeleteRequest where @@ -58,9 +70,11 @@ where #[serde(flatten, bound = "")] pub common: CommonRequestParams, + // No `other` catch-all here; any additional fields will be caught in `common.other` instead } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CommonRequestParams where @@ -72,9 +86,17 @@ where pub resource_type: String, pub request_id: String, pub stack_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudFormationCustomResourceResponse where @@ -84,6 +106,13 @@ where #[serde(bound = "")] pub data: D, pub no_echo: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/cloudwatch_alarms/mod.rs b/lambda-events/src/event/cloudwatch_alarms/mod.rs index d99f3c94d..46c9503a8 100644 --- a/lambda-events/src/event/cloudwatch_alarms/mod.rs +++ b/lambda-events/src/event/cloudwatch_alarms/mod.rs @@ -11,7 +11,8 @@ use serde_json::Value; /// `CloudWatchAlarm` is the generic outer structure of an event triggered by a CloudWatch Alarm. /// You probably want to use `CloudWatchMetricAlarm` or `CloudWatchCompositeAlarm` if you know which kind of alarm your function is receiving. /// For examples of events that come via CloudWatch Alarms, -/// see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html#Lambda-action-payload +/// see +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarm @@ -33,6 +34,13 @@ where #[serde(default, bound = "")] pub alarm_data: CloudWatchAlarmData, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CloudWatchMetricAlarm` is the structure of an event triggered by CloudWatch metric alarms. @@ -43,6 +51,7 @@ pub type CloudWatchMetricAlarm = pub type CloudWatchCompositeAlarm = CloudWatchAlarm; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarmData @@ -59,8 +68,16 @@ where pub previous_state: Option>, #[serde(bound = "")] pub configuration: C, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarmState @@ -76,8 +93,16 @@ where pub timestamp: DateTime, pub actions_suppressed_by: Option, pub actions_suppressed_reason: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchMetricAlarmConfiguration { @@ -85,8 +110,16 @@ pub struct CloudWatchMetricAlarmConfiguration { pub description: Option, #[serde(default)] pub metrics: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchMetricDefinition { @@ -94,8 +127,16 @@ pub struct CloudWatchMetricDefinition { #[serde(default)] pub return_data: bool, pub metric_stat: CloudWatchMetricStatDefinition, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchMetricStatDefinition { @@ -105,8 +146,16 @@ pub struct CloudWatchMetricStatDefinition { pub stat: Option, pub period: u16, pub metric: CloudWatchMetricStatMetricDefinition, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchMetricStatMetricDefinition { @@ -114,8 +163,16 @@ pub struct CloudWatchMetricStatMetricDefinition { pub namespace: Option, pub name: String, pub dimensions: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchCompositeAlarmConfiguration { @@ -123,8 +180,16 @@ pub struct CloudWatchCompositeAlarmConfiguration { pub actions_suppressor: String, pub actions_suppressor_wait_period: u16, pub actions_suppressor_extension_period: u16, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum CloudWatchAlarmStateValue { @@ -134,6 +199,7 @@ pub enum CloudWatchAlarmStateValue { InsufficientData, } +#[non_exhaustive] #[derive(Clone, Debug, PartialEq)] pub enum CloudWatchAlarmStateReasonData { Metric(CloudWatchAlarmStateReasonDataMetric), @@ -147,6 +213,7 @@ impl Default for CloudWatchAlarmStateReasonData { } } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarmStateReasonDataMetric { @@ -169,8 +236,16 @@ pub struct CloudWatchAlarmStateReasonDataMetric { pub threshold: f64, #[serde(default)] pub evaluated_datapoints: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarmStateEvaluatedDatapoint { @@ -181,28 +256,59 @@ pub struct CloudWatchAlarmStateEvaluatedDatapoint { pub value: Option, #[serde(default)] pub threshold: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ClodWatchAlarmStateReasonDataComposite { #[serde(default)] pub triggering_alarms: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarmStateTriggeringAlarm { pub arn: String, pub state: CloudWatchAlarmStateTriggeringAlarmState, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchAlarmStateTriggeringAlarmState { pub timestamp: DateTime, #[serde(default)] pub value: CloudWatchAlarmStateValue, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } impl<'de> Deserialize<'de> for CloudWatchAlarmStateReasonData { @@ -224,7 +330,7 @@ impl Serialize for CloudWatchAlarmStateReasonData { Self::Composite(m) => serde_json::to_string(m), Self::Generic(m) => serde_json::to_string(m), }; - let s = r.map_err(|e| SerError::custom(format!("failed to serialize struct as string {}", e)))?; + let s = r.map_err(|e| SerError::custom(format!("failed to serialize struct as string {e}")))?; serializer.serialize_str(&s) } diff --git a/lambda-events/src/event/cloudwatch_events/cloudtrail.rs b/lambda-events/src/event/cloudwatch_events/cloudtrail.rs index fc3323065..70275c4f5 100644 --- a/lambda-events/src/event/cloudwatch_events/cloudtrail.rs +++ b/lambda-events/src/event/cloudwatch_events/cloudtrail.rs @@ -2,6 +2,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AWSAPICall { @@ -23,8 +24,16 @@ pub struct AWSAPICall { #[serde(rename = "eventID")] pub event_id: String, pub event_type: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SessionIssuer { @@ -33,22 +42,46 @@ pub struct SessionIssuer { pub principal_id: String, pub arn: String, pub account_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct WebIdFederationData { pub federated_provider: Option, pub attributes: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Attributes { pub mfa_authenticated: String, pub creation_date: DateTime, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SessionContext { @@ -57,16 +90,32 @@ pub struct SessionContext { pub attributes: Attributes, pub source_identity: Option, pub ec2_role_delivery: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct OnBehalfOf { pub user_id: String, pub identity_store_arn: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } // https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UserIdentity { @@ -79,6 +128,13 @@ pub struct UserIdentity { pub session_context: Option, pub user_name: Option, pub on_behalf_of: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/cloudwatch_events/codedeploy.rs b/lambda-events/src/event/cloudwatch_events/codedeploy.rs index 1bd442976..f01a5bba4 100644 --- a/lambda-events/src/event/cloudwatch_events/codedeploy.rs +++ b/lambda-events/src/event/cloudwatch_events/codedeploy.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StateChangeNotification { @@ -9,8 +12,16 @@ pub struct StateChangeNotification { pub deployment_id: String, pub state: String, pub deployment_group: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DeploymentStateChangeNotification { @@ -21,8 +32,16 @@ pub struct DeploymentStateChangeNotification { pub deployment_id: String, pub instance_group_id: String, pub deployment_group: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct InstanceStateChangeNotification { @@ -31,4 +50,11 @@ pub struct InstanceStateChangeNotification { pub state: String, #[serde(rename = "execution-id")] pub execution_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/codepipeline.rs b/lambda-events/src/event/cloudwatch_events/codepipeline.rs index ce5fa47ca..89f2029c2 100644 --- a/lambda-events/src/event/cloudwatch_events/codepipeline.rs +++ b/lambda-events/src/event/cloudwatch_events/codepipeline.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct PipelineExecutionStateChange { @@ -8,8 +11,16 @@ pub struct PipelineExecutionStateChange { pub state: String, #[serde(rename = "execution-id")] pub execution_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StageExecutionStateChange { @@ -19,8 +30,16 @@ pub struct StageExecutionStateChange { pub execution_id: String, pub stage: String, pub state: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ActionExecutionStateChange { @@ -34,8 +53,16 @@ pub struct ActionExecutionStateChange { pub region: String, #[serde(rename = "type")] pub type_field: ActionExecutionStateChangeType, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ActionExecutionStateChangeType { @@ -43,4 +70,11 @@ pub struct ActionExecutionStateChangeType { pub category: String, pub provider: String, pub version: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/ec2.rs b/lambda-events/src/event/cloudwatch_events/ec2.rs index c8eb7834f..378b64f1b 100644 --- a/lambda-events/src/event/cloudwatch_events/ec2.rs +++ b/lambda-events/src/event/cloudwatch_events/ec2.rs @@ -1,9 +1,19 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct InstanceStateChange { #[serde(rename = "instance-id")] pub instance_id: String, pub state: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/emr.rs b/lambda-events/src/event/cloudwatch_events/emr.rs index 87fb80857..f30d5334c 100644 --- a/lambda-events/src/event/cloudwatch_events/emr.rs +++ b/lambda-events/src/event/cloudwatch_events/emr.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AutoScalingPolicyStateChange { @@ -8,8 +11,16 @@ pub struct AutoScalingPolicyStateChange { pub state: String, pub message: String, pub scaling_resource_type: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ClusterStateChange { @@ -19,8 +30,16 @@ pub struct ClusterStateChange { pub cluster_id: String, pub state: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct InstanceGroupStateChange { @@ -34,8 +53,16 @@ pub struct InstanceGroupStateChange { pub running_instance_count: String, pub state: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StepStatusChange { @@ -46,4 +73,11 @@ pub struct StepStatusChange { pub cluster_id: String, pub state: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/gamelift.rs b/lambda-events/src/event/cloudwatch_events/gamelift.rs index fb5c50a75..956783290 100644 --- a/lambda-events/src/event/cloudwatch_events/gamelift.rs +++ b/lambda-events/src/event/cloudwatch_events/gamelift.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use crate::custom_serde::deserialize_nullish_boolean; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MatchmakingSearching { @@ -9,16 +12,32 @@ pub struct MatchmakingSearching { pub estimated_wait_millis: String, pub r#type: String, pub game_session_info: GameSessionInfo, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Ticket { pub ticket_id: String, pub start_time: String, pub players: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Player { @@ -27,14 +46,30 @@ pub struct Player { #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub accepted: bool, pub player_session_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct GameSessionInfo { pub players: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct PotentialMatchCreated { @@ -45,16 +80,32 @@ pub struct PotentialMatchCreated { pub r#type: String, pub game_session_info: GameSessionInfo, pub match_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct RuleEvaluationMetric { pub rule_name: String, pub passed_count: i64, pub failed_count: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AcceptMatch { @@ -62,8 +113,16 @@ pub struct AcceptMatch { pub r#type: String, pub game_session_info: GameSessionInfo, pub match_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AcceptMatchCompleted { @@ -72,8 +131,16 @@ pub struct AcceptMatchCompleted { pub r#type: String, pub game_session_info: GameSessionInfo, pub match_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MatchmakingSucceeded { @@ -81,8 +148,16 @@ pub struct MatchmakingSucceeded { pub r#type: String, pub game_session_info: GameSessionInfo, pub match_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MatchmakingTimedOut { @@ -92,8 +167,16 @@ pub struct MatchmakingTimedOut { pub r#type: String, pub message: String, pub game_session_info: GameSessionInfo, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MatchmakingCancelled { @@ -103,8 +186,16 @@ pub struct MatchmakingCancelled { pub r#type: String, pub message: String, pub game_session_info: GameSessionInfo, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MatchmakingFailed { @@ -115,4 +206,11 @@ pub struct MatchmakingFailed { pub message: String, pub game_session_info: GameSessionInfo, pub match_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/glue.rs b/lambda-events/src/event/cloudwatch_events/glue.rs index 08f059292..69d428b20 100644 --- a/lambda-events/src/event/cloudwatch_events/glue.rs +++ b/lambda-events/src/event/cloudwatch_events/glue.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct JobRunStateChange { @@ -8,8 +11,16 @@ pub struct JobRunStateChange { pub state: String, pub job_run_id: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CrawlerStarted { @@ -18,8 +29,16 @@ pub struct CrawlerStarted { pub start_time: String, pub state: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CrawlerSucceeded { @@ -38,8 +57,16 @@ pub struct CrawlerSucceeded { pub state: String, pub partitions_created: String, pub cloud_watch_log_link: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CrawlerFailed { @@ -49,8 +76,16 @@ pub struct CrawlerFailed { pub cloud_watch_log_link: String, pub state: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct JobRunStatus { @@ -61,15 +96,31 @@ pub struct JobRunStatus { pub job_run_id: String, pub message: String, pub started_on: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct NotificationCondition { #[serde(rename = "NotifyDelayAfter")] pub notify_delay_after: f64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DataCatalogTableStateChange { @@ -77,12 +128,27 @@ pub struct DataCatalogTableStateChange { pub changed_partitions: Vec, pub type_of_change: String, pub table_name: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DataCatalogDatabaseStateChange { pub database_name: String, pub type_of_change: String, pub changed_tables: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/health.rs b/lambda-events/src/event/cloudwatch_events/health.rs index 2a6a82e37..af5ebfc4b 100644 --- a/lambda-events/src/event/cloudwatch_events/health.rs +++ b/lambda-events/src/event/cloudwatch_events/health.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Event { @@ -12,18 +15,41 @@ pub struct Event { pub end_time: String, pub event_description: Vec, pub affected_entities: Option>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EventDescription { pub language: String, pub latest_description: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Entity { pub entity_value: String, pub tags: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/kms.rs b/lambda-events/src/event/cloudwatch_events/kms.rs index 74a76e701..c96d2be59 100644 --- a/lambda-events/src/event/cloudwatch_events/kms.rs +++ b/lambda-events/src/event/cloudwatch_events/kms.rs @@ -1,8 +1,18 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CMKEvent { #[serde(rename = "key-id")] pub key_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/macie.rs b/lambda-events/src/event/cloudwatch_events/macie.rs index 37d18d7a0..7a1a989b9 100644 --- a/lambda-events/src/event/cloudwatch_events/macie.rs +++ b/lambda-events/src/event/cloudwatch_events/macie.rs @@ -1,6 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +#[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] +use serde_json::Value; use std::collections::HashMap; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Alert { @@ -18,12 +22,20 @@ pub struct Alert { pub created_at: String, pub actor: String, pub summary: T, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type BucketScanAlert = Alert; pub type BucketWritableAlert = Alert; pub type BucketContainsHighRiskObjectAlert = Alert; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Trigger { @@ -35,8 +47,16 @@ pub struct Trigger { pub created_at: String, pub description: String, pub risk: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct BucketScanSummary { @@ -57,8 +77,16 @@ pub struct BucketScanSummary { #[serde(rename = "Events")] pub events: HashMap, pub recipient_account_id: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Ip { @@ -68,23 +96,47 @@ pub struct Ip { pub n34_205_153_2: i64, #[serde(rename = "72.21.196.70")] pub n72_21_196_70: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TimeRange { pub count: i64, pub start: String, pub end: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Location { #[serde(rename = "us-east-1")] pub us_east_1: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ActionInfo { @@ -92,8 +144,16 @@ pub struct ActionInfo { #[serde(rename = "ISP")] pub isp: HashMap, pub error_code: Option>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct BucketWritableSummary { @@ -109,22 +169,46 @@ pub struct BucketWritableSummary { pub event_count: i64, #[serde(rename = "Timestamps")] pub timestamps: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Bucket { #[serde(rename = "secret-bucket-name")] pub secret_bucket_name: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Acl { #[serde(rename = "secret-bucket-name")] pub secret_bucket_name: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SecretBucketName { @@ -132,8 +216,16 @@ pub struct SecretBucketName { pub owner: Owner, #[serde(rename = "Grants")] pub grants: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Owner { @@ -141,8 +233,16 @@ pub struct Owner { pub display_name: String, #[serde(rename = "ID")] pub id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Grant { @@ -150,16 +250,32 @@ pub struct Grant { pub grantee: Grantee, #[serde(rename = "Permission")] pub permission: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Grantee { pub r#type: String, #[serde(rename = "URI")] pub uri: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct BucketContainsHighRiskObjectSummary { @@ -179,8 +295,16 @@ pub struct BucketContainsHighRiskObjectSummary { pub owner: HashMap, #[serde(rename = "Timestamps")] pub timestamps: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AlertUpdated { @@ -197,16 +321,32 @@ pub struct AlertUpdated { pub created_at: String, pub actor: String, pub trigger: UpdatedTrigger, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UpdatedTrigger { #[serde(rename = "alert-type")] pub alert_type: String, pub features: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct FeatureInfo { @@ -218,4 +358,11 @@ pub struct FeatureInfo { #[serde(rename = "excession_times")] pub excession_times: Vec, pub risk: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/mod.rs b/lambda-events/src/event/cloudwatch_events/mod.rs index 6384406e0..91f7e7fd9 100644 --- a/lambda-events/src/event/cloudwatch_events/mod.rs +++ b/lambda-events/src/event/cloudwatch_events/mod.rs @@ -20,8 +20,9 @@ pub mod tag; pub mod trustedadvisor; /// `CloudWatchEvent` is the outer structure of an event sent via CloudWatch Events. -/// For examples of events that come via CloudWatch Events, see https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +/// For examples of events that come via CloudWatch Events, see +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CloudWatchEvent where @@ -46,4 +47,11 @@ where pub resources: Vec, #[serde(bound = "")] pub detail: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/opsworks.rs b/lambda-events/src/event/cloudwatch_events/opsworks.rs index d1c192e53..7c26baaf5 100644 --- a/lambda-events/src/event/cloudwatch_events/opsworks.rs +++ b/lambda-events/src/event/cloudwatch_events/opsworks.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct InstanceStateChange { @@ -15,8 +18,16 @@ pub struct InstanceStateChange { #[serde(rename = "ec2-instance-id")] pub ec2_instance_id: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CommandStateChange { @@ -26,8 +37,16 @@ pub struct CommandStateChange { pub instance_id: String, pub r#type: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DeploymentStateChange { @@ -40,8 +59,16 @@ pub struct DeploymentStateChange { pub deployment_id: String, pub command: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Alert { @@ -51,4 +78,11 @@ pub struct Alert { pub instance_id: String, pub r#type: String, pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/signin.rs b/lambda-events/src/event/cloudwatch_events/signin.rs index 1cd73e6e7..458787bd2 100644 --- a/lambda-events/src/event/cloudwatch_events/signin.rs +++ b/lambda-events/src/event/cloudwatch_events/signin.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SignIn { @@ -19,8 +20,16 @@ pub struct SignIn { #[serde(rename = "eventID")] pub event_id: String, pub event_type: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UserIdentity { @@ -28,15 +37,31 @@ pub struct UserIdentity { pub principal_id: String, pub arn: String, pub account_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ResponseElements { #[serde(rename = "ConsoleLogin")] pub console_login: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AdditionalEventData { @@ -46,4 +71,11 @@ pub struct AdditionalEventData { pub mobile_version: String, #[serde(rename = "MFAUsed")] pub mfaused: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/sms.rs b/lambda-events/src/event/cloudwatch_events/sms.rs index 7d1618229..e3bfeeb40 100644 --- a/lambda-events/src/event/cloudwatch_events/sms.rs +++ b/lambda-events/src/event/cloudwatch_events/sms.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct JobStateChange { @@ -11,4 +14,11 @@ pub struct JobStateChange { #[serde(rename = "ami-id")] pub ami_id: Option, pub version: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/ssm.rs b/lambda-events/src/event/cloudwatch_events/ssm.rs index fa6ffc3b0..1bf81ed2c 100644 --- a/lambda-events/src/event/cloudwatch_events/ssm.rs +++ b/lambda-events/src/event/cloudwatch_events/ssm.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EC2AutomationStepStatusChange { @@ -22,8 +25,16 @@ pub struct EC2AutomationStepStatusChange { pub step_name: String, #[serde(rename = "Action")] pub action: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EC2AutomationExecutionStatusChange { @@ -43,16 +54,32 @@ pub struct EC2AutomationExecutionStatusChange { pub time: f64, #[serde(rename = "ExecutedBy")] pub executed_by: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StateChange { pub state: String, pub at_time: String, pub next_transition_time: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConfigurationComplianceStateChange { @@ -69,8 +96,16 @@ pub struct ConfigurationComplianceStateChange { #[serde(rename = "patch-baseline-id")] pub patch_baseline_id: Option, pub serverity: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MaintenanceWindowTargetRegistration { @@ -79,8 +114,16 @@ pub struct MaintenanceWindowTargetRegistration { #[serde(rename = "window-id")] pub window_id: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MaintenanceWindowExecutionStateChange { @@ -93,8 +136,16 @@ pub struct MaintenanceWindowExecutionStateChange { #[serde(rename = "window-execution-id")] pub window_execution_id: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MaintenanceWindowTaskExecutionStateChange { @@ -109,8 +160,16 @@ pub struct MaintenanceWindowTaskExecutionStateChange { #[serde(rename = "window-execution-id")] pub window_execution_id: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MaintenanceWindowTaskTargetInvocationStateChange { @@ -129,16 +188,32 @@ pub struct MaintenanceWindowTaskTargetInvocationStateChange { pub status: String, #[serde(rename = "owner-information")] pub owner_information: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct MaintenanceWindowStateChange { #[serde(rename = "window-id")] pub window_id: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ParameterStoreStateChange { @@ -146,8 +221,16 @@ pub struct ParameterStoreStateChange { pub name: String, pub r#type: String, pub description: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EC2CommandStatusChange { @@ -161,8 +244,16 @@ pub struct EC2CommandStatusChange { #[serde(rename = "requested-date-time")] pub requested_date_time: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EC2CommandInvocationStatusChange { @@ -175,8 +266,16 @@ pub struct EC2CommandInvocationStatusChange { #[serde(rename = "requested-date-time")] pub requested_date_time: String, pub status: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EC2StateManagerAssociationStateChange { @@ -204,8 +303,16 @@ pub struct EC2StateManagerAssociationStateChange { pub schedule_expression: String, #[serde(rename = "association-cwe-version")] pub association_cwe_version: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EC2StateManagerInstanceAssociationStateChange { @@ -235,4 +342,11 @@ pub struct EC2StateManagerInstanceAssociationStateChange { pub output_url: String, #[serde(rename = "instance-association-cwe-version")] pub instance_association_cwe_version: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/tag.rs b/lambda-events/src/event/cloudwatch_events/tag.rs index d5bc96815..e6d3b96fd 100644 --- a/lambda-events/src/event/cloudwatch_events/tag.rs +++ b/lambda-events/src/event/cloudwatch_events/tag.rs @@ -1,7 +1,10 @@ +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use serde::{Deserialize, Serialize}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TagChangeOnResource { @@ -12,4 +15,11 @@ pub struct TagChangeOnResource { pub resource_type: String, pub version: i64, pub tags: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_events/trustedadvisor.rs b/lambda-events/src/event/cloudwatch_events/trustedadvisor.rs index 6a7e25d39..fdaf4a821 100644 --- a/lambda-events/src/event/cloudwatch_events/trustedadvisor.rs +++ b/lambda-events/src/event/cloudwatch_events/trustedadvisor.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CheckItemRefreshNotification { @@ -12,4 +15,11 @@ pub struct CheckItemRefreshNotification { #[serde(rename = "resource_id")] pub resource_id: String, pub uuid: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/cloudwatch_logs/mod.rs b/lambda-events/src/event/cloudwatch_logs/mod.rs index 0c9ad4a8a..e494918a4 100644 --- a/lambda-events/src/event/cloudwatch_logs/mod.rs +++ b/lambda-events/src/event/cloudwatch_logs/mod.rs @@ -3,17 +3,30 @@ use serde::{ ser::{Error as SeError, SerializeStruct}, Deserialize, Deserializer, Serialize, Serializer, }; +#[cfg(feature = "catch-all-fields")] +#[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] +use serde_json::Value; + use std::{fmt, io::BufReader}; /// `LogsEvent` represents the raw event sent by CloudWatch +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct LogsEvent { /// `aws_logs` is gzipped and base64 encoded, it needs a custom deserializer #[serde(rename = "awslogs")] pub aws_logs: AwsLogs, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `AwsLogs` is an unmarshaled, ungzipped, CloudWatch logs event +#[non_exhaustive] #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct AwsLogs { /// `data` is the log data after is decompressed @@ -21,6 +34,7 @@ pub struct AwsLogs { } /// `LogData` represents the logs group event information +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct LogData { @@ -36,9 +50,17 @@ pub struct LogData { pub message_type: String, /// Entries in the log batch pub log_events: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `LogEntry` represents a log entry from cloudwatch logs +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct LogEntry { /// Unique id for the entry @@ -47,6 +69,13 @@ pub struct LogEntry { pub timestamp: i64, /// Message published in the application log pub message: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } impl<'de> Deserialize<'de> for AwsLogs { diff --git a/lambda-events/src/event/code_commit/mod.rs b/lambda-events/src/event/code_commit/mod.rs index 7d5edfaaf..126d71609 100644 --- a/lambda-events/src/event/code_commit/mod.rs +++ b/lambda-events/src/event/code_commit/mod.rs @@ -1,20 +1,31 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use crate::custom_serde::deserialize_nullish_boolean; /// `CodeCommitEvent` represents a CodeCommit event -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeCommitEvent { #[serde(rename = "Records")] pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type CodeCommitEventTime = DateTime; /// `CodeCommitRecord` represents a CodeCommit record -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeCommitRecord { #[serde(default)] @@ -44,17 +55,36 @@ pub struct CodeCommitRecord { pub aws_region: Option, pub event_total_parts: u64, pub custom_data: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeCommitCodeCommit` represents a CodeCommit object in a record -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeCommitCodeCommit { pub references: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeCommitReference` represents a Reference object in a CodeCommit object -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeCommitReference { #[serde(default)] @@ -63,6 +93,13 @@ pub struct CodeCommitReference { pub ref_: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub created: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/codebuild/mod.rs b/lambda-events/src/event/codebuild/mod.rs index d4970f5a3..27a0e060d 100644 --- a/lambda-events/src/event/codebuild/mod.rs +++ b/lambda-events/src/event/codebuild/mod.rs @@ -11,8 +11,9 @@ pub type CodeBuildPhaseStatus = String; pub type CodeBuildPhaseType = String; /// `CodeBuildEvent` is documented at: -/// https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html#sample-build-notifications-ref -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +/// +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildEvent { /// AccountID is the id of the AWS account from which the event originated. @@ -44,10 +45,18 @@ pub struct CodeBuildEvent { /// Detail contains information specific to a build state-change or /// build phase-change event. pub detail: CodeBuildEventDetail, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildEventDetail` represents the all details related to the code build event -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildEventDetail { #[serde(rename = "build-status")] @@ -84,10 +93,18 @@ pub struct CodeBuildEventDetail { #[serde(default)] #[serde(with = "codebuild_time::optional_time")] pub completed_phase_end: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildEventAdditionalInformation` represents additional information to the code build event -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildEventAdditionalInformation { pub artifact: CodeBuildArtifact, @@ -109,10 +126,18 @@ pub struct CodeBuildEventAdditionalInformation { pub source_version: Option, pub logs: CodeBuildLogs, pub phases: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildArtifact` represents the artifact provided to build -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildArtifact { #[serde(default)] @@ -123,10 +148,18 @@ pub struct CodeBuildArtifact { pub sha256_sum: Option, #[serde(default)] pub location: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildEnvironment` represents the environment for a build -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildEnvironment { #[serde(default)] @@ -140,10 +173,18 @@ pub struct CodeBuildEnvironment { pub type_: Option, #[serde(rename = "environment-variables")] pub environment_variables: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildEnvironmentVariable` encapsulate environment variables for the code build -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildEnvironmentVariable { /// Name is the name of the environment variable. @@ -155,20 +196,36 @@ pub struct CodeBuildEnvironmentVariable { /// Value is the value of the environment variable. #[serde(default)] pub value: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildSource` represent the code source will be build -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildSource { #[serde(default)] pub location: Option, #[serde(default)] pub type_: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildLogs` gives the log details of a code build -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildLogs { #[serde(default)] @@ -180,10 +237,18 @@ pub struct CodeBuildLogs { #[serde(default)] #[serde(rename = "deep-link")] pub deep_link: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodeBuildPhase` represents the phase of a build and its details -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeBuildPhase where @@ -206,6 +271,13 @@ where pub phase_type: CodeBuildPhaseType, #[serde(rename = "phase-status")] pub phase_status: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type CodeBuildTime = DateTime; diff --git a/lambda-events/src/event/codedeploy/mod.rs b/lambda-events/src/event/codedeploy/mod.rs index d51bf8aa6..debe2bf54 100644 --- a/lambda-events/src/event/codedeploy/mod.rs +++ b/lambda-events/src/event/codedeploy/mod.rs @@ -1,11 +1,14 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; pub type CodeDeployDeploymentState = String; /// `CodeDeployEvent` is documented at: -/// https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html#acd_event_types -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +/// +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeDeployEvent { /// AccountID is the id of the AWS account from which the event originated. @@ -38,9 +41,17 @@ pub struct CodeDeployEvent { pub resources: Vec, /// Detail contains information specific to a deployment event. pub detail: CodeDeployEventDetail, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeDeployEventDetail { /// InstanceGroupID is the ID of the instance group. @@ -63,13 +74,28 @@ pub struct CodeDeployEventDetail { /// DeploymentGroup is the name of the deployment group. #[serde(default)] pub deployment_group: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Deserialize, Serialize, Eq, PartialEq)] #[serde(rename_all = "PascalCase")] pub struct CodeDeployLifecycleEvent { pub deployment_id: String, pub lifecycle_event_hook_execution_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/codepipeline_cloudwatch/mod.rs b/lambda-events/src/event/codepipeline_cloudwatch/mod.rs index 22db26b15..087d1452e 100644 --- a/lambda-events/src/event/codepipeline_cloudwatch/mod.rs +++ b/lambda-events/src/event/codepipeline_cloudwatch/mod.rs @@ -1,5 +1,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; pub type CodePipelineStageState = String; @@ -8,8 +10,9 @@ pub type CodePipelineState = String; pub type CodePipelineActionState = String; /// CodePipelineEvent is documented at: -/// https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html#codepipeline_event_type -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +/// +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineCloudWatchEvent { /// Version is the version of the event's schema. @@ -42,9 +45,17 @@ pub struct CodePipelineCloudWatchEvent { pub resources: Vec, /// Detail contains information specific to a deployment event. pub detail: CodePipelineEventDetail, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineEventDetail { #[serde(default)] @@ -62,9 +73,17 @@ pub struct CodePipelineEventDetail { #[serde(default)] pub region: Option, pub type_: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineEventDetailType { #[serde(default)] @@ -75,6 +94,13 @@ pub struct CodePipelineEventDetailType { pub provider: Option, /// From published EventBridge schema registry this is always int64 not string as documented pub version: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/codepipeline_job/mod.rs b/lambda-events/src/event/codepipeline_job/mod.rs index 888e77b7f..83dfce656 100644 --- a/lambda-events/src/event/codepipeline_job/mod.rs +++ b/lambda-events/src/event/codepipeline_job/mod.rs @@ -1,15 +1,26 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `CodePipelineJobEvent` contains data from an event sent from AWS CodePipeline -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineJobEvent { #[serde(rename = "CodePipeline.job")] pub code_pipeline_job: CodePipelineJob, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineJob` represents a job from an AWS CodePipeline event -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineJob { #[serde(default)] @@ -17,10 +28,18 @@ pub struct CodePipelineJob { #[serde(default)] pub account_id: Option, pub data: CodePipelineData, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineData` represents a job from an AWS CodePipeline event -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineData { pub action_configuration: CodePipelineActionConfiguration, @@ -30,17 +49,33 @@ pub struct CodePipelineData { pub artifact_credentials: CodePipelineArtifactCredentials, #[serde(default)] pub continuation_token: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineActionConfiguration` represents an Action Configuration -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineActionConfiguration { pub configuration: CodePipelineConfiguration, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineConfiguration` represents a configuration for an Action Configuration -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineConfiguration { #[serde(default)] @@ -49,60 +84,108 @@ pub struct CodePipelineConfiguration { #[serde(default)] #[serde(rename = "UserParameters")] pub user_parameters: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineInputArtifact` represents an input artifact -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineInputArtifact { pub location: CodePipelineInputLocation, pub revision: Option, #[serde(default)] pub name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineInputLocation` represents a input location -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineInputLocation { pub s3_location: CodePipelineS3Location, #[serde(default)] #[serde(rename = "type")] pub location_type: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineS3Location` represents an s3 input location -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineS3Location { #[serde(default)] pub bucket_name: Option, #[serde(default)] pub object_key: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineOutputArtifact` represents an output artifact -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineOutputArtifact { pub location: CodePipelineInputLocation, pub revision: Option, #[serde(default)] pub name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineOutputLocation` represents a output location -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineOutputLocation { pub s3_location: CodePipelineS3Location, #[serde(default)] #[serde(rename = "type")] pub location_type: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CodePipelineArtifactCredentials` represents CodePipeline artifact credentials -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodePipelineArtifactCredentials { #[serde(default)] @@ -111,6 +194,13 @@ pub struct CodePipelineArtifactCredentials { pub session_token: Option, #[serde(default)] pub access_key_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/cognito/mod.rs b/lambda-events/src/event/cognito/mod.rs index a0ebd8d9f..d656bb592 100644 --- a/lambda-events/src/event/cognito/mod.rs +++ b/lambda-events/src/event/cognito/mod.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use crate::custom_serde::{deserialize_lambda_map, deserialize_nullish_boolean}; /// `CognitoEvent` contains data from an event sent from AWS Cognito Sync +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEvent { @@ -22,9 +23,17 @@ pub struct CognitoEvent { #[serde(default)] pub region: Option, pub version: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoDatasetRecord` represents a record from an AWS Cognito Sync event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoDatasetRecord { @@ -34,10 +43,18 @@ pub struct CognitoDatasetRecord { pub old_value: Option, #[serde(default)] pub op: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreSignup` is sent by AWS Cognito User Pools when a user attempts to register /// (sign up), allowing a Lambda to perform custom validation to accept or deny the registration request +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreSignup { @@ -46,8 +63,16 @@ pub struct CognitoEventUserPoolsPreSignup { pub cognito_event_user_pools_header: CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsPreSignupRequest, pub response: CognitoEventUserPoolsPreSignupResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsPreSignupTriggerSource { #[serde(rename = "PreSignUp_SignUp")] @@ -61,6 +86,7 @@ pub enum CognitoEventUserPoolsPreSignupTriggerSource { /// `CognitoEventUserPoolsPreAuthentication` is sent by AWS Cognito User Pools when a user submits their information /// to be authenticated, allowing you to perform custom validations to accept or deny the sign in request. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreAuthentication { @@ -70,8 +96,16 @@ pub struct CognitoEventUserPoolsPreAuthentication { CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsPreAuthenticationRequest, pub response: CognitoEventUserPoolsPreAuthenticationResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsPreAuthenticationTriggerSource { #[serde(rename = "PreAuthentication_Authentication")] @@ -81,6 +115,7 @@ pub enum CognitoEventUserPoolsPreAuthenticationTriggerSource { /// `CognitoEventUserPoolsPostConfirmation` is sent by AWS Cognito User Pools after a user is confirmed, /// allowing the Lambda to send custom messages or add custom logic. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPostConfirmation @@ -95,8 +130,16 @@ where pub request: CognitoEventUserPoolsPostConfirmationRequest, #[serde(bound = "")] pub response: T, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsPostConfirmationTriggerSource { #[serde(rename = "PostConfirmation_ConfirmForgotPassword")] @@ -108,6 +151,7 @@ pub enum CognitoEventUserPoolsPostConfirmationTriggerSource { /// `CognitoEventUserPoolsPreTokenGen` is sent by AWS Cognito User Pools when a user attempts to retrieve /// credentials, allowing a Lambda to perform insert, suppress or override claims +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreTokenGen { @@ -116,8 +160,16 @@ pub struct CognitoEventUserPoolsPreTokenGen { pub cognito_event_user_pools_header: CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsPreTokenGenRequest, pub response: CognitoEventUserPoolsPreTokenGenResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsPreTokenGenTriggerSource { #[serde(rename = "TokenGeneration_HostedAuth")] @@ -135,6 +187,7 @@ pub enum CognitoEventUserPoolsPreTokenGenTriggerSource { /// `CognitoEventUserPoolsPostAuthentication` is sent by AWS Cognito User Pools after a user is authenticated, /// allowing the Lambda to add custom logic. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPostAuthentication { @@ -144,8 +197,16 @@ pub struct CognitoEventUserPoolsPostAuthentication { CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsPostAuthenticationRequest, pub response: CognitoEventUserPoolsPostAuthenticationResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsPostAuthenticationTriggerSource { #[serde(rename = "PostAuthentication_Authentication")] @@ -155,6 +216,7 @@ pub enum CognitoEventUserPoolsPostAuthenticationTriggerSource { /// `CognitoEventUserPoolsMigrateUser` is sent by AWS Cognito User Pools when a user does not exist in the /// user pool at the time of sign-in with a password, or in the forgot-password flow. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsMigrateUser { @@ -165,8 +227,16 @@ pub struct CognitoEventUserPoolsMigrateUser { pub cognito_event_user_pools_migrate_user_request: CognitoEventUserPoolsMigrateUserRequest, #[serde(rename = "response")] pub cognito_event_user_pools_migrate_user_response: CognitoEventUserPoolsMigrateUserResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsMigrateUserTriggerSource { #[serde(rename = "UserMigration_Authentication")] @@ -177,6 +247,7 @@ pub enum CognitoEventUserPoolsMigrateUserTriggerSource { } /// `CognitoEventUserPoolsCallerContext` contains information about the caller +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCallerContext { @@ -185,9 +256,17 @@ pub struct CognitoEventUserPoolsCallerContext { pub awssdk_version: Option, #[serde(default)] pub client_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsHeader` contains common data from events sent by AWS Cognito User Pools +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsHeader { @@ -202,9 +281,13 @@ pub struct CognitoEventUserPoolsHeader { pub caller_context: CognitoEventUserPoolsCallerContext, #[serde(default)] pub user_name: Option, + // no `other` catch-all, because this struct is itself #[serde(flatten)]-ed + // into a different struct that contains an `other` catch-all, so any + // additional fields will be caught there instead } /// `CognitoEventUserPoolsPreSignupRequest` contains the request portion of a PreSignup event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreSignupRequest { @@ -217,18 +300,34 @@ pub struct CognitoEventUserPoolsPreSignupRequest { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub client_metadata: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreSignupResponse` contains the response portion of a PreSignup event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreSignupResponse { pub auto_confirm_user: bool, pub auto_verify_email: bool, pub auto_verify_phone: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreAuthenticationRequest` contains the request portion of a PreAuthentication event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreAuthenticationRequest { @@ -238,12 +337,29 @@ pub struct CognitoEventUserPoolsPreAuthenticationRequest { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub validation_data: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreAuthenticationResponse` contains the response portion of a PreAuthentication event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct CognitoEventUserPoolsPreAuthenticationResponse {} +pub struct CognitoEventUserPoolsPreAuthenticationResponse { + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} /// `CognitoEventUserPoolsPostConfirmationRequest` contains the request portion of a PostConfirmation event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPostConfirmationRequest { @@ -253,13 +369,30 @@ pub struct CognitoEventUserPoolsPostConfirmationRequest { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub client_metadata: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPostConfirmationResponse` contains the response portion of a PostConfirmation event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct CognitoEventUserPoolsPostConfirmationResponse {} +pub struct CognitoEventUserPoolsPostConfirmationResponse { + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} /// `CognitoEventUserPoolsPreTokenGenRequest` contains request portion of PreTokenGen event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreTokenGenRequest { @@ -270,17 +403,33 @@ pub struct CognitoEventUserPoolsPreTokenGenRequest { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub client_metadata: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreTokenGenResponse` contains the response portion of a PreTokenGen event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreTokenGenResponse { pub claims_override_details: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreTokenGenV2` is sent by AWS Cognito User Pools when a user attempts to retrieve /// credentials, allowing a Lambda to perform insert, suppress or override claims. This is the Version 2 Payload +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreTokenGenV2 { @@ -289,9 +438,17 @@ pub struct CognitoEventUserPoolsPreTokenGenV2 { pub cognito_event_user_pools_header: CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsPreTokenGenRequestV2, pub response: CognitoEventUserPoolsPreTokenGenResponseV2, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPreTokenGenRequestV2` contains request portion of PreTokenGenV2 event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreTokenGenRequestV2 { @@ -303,32 +460,64 @@ pub struct CognitoEventUserPoolsPreTokenGenRequestV2 { #[serde(default)] pub client_metadata: HashMap, pub scopes: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPreTokenGenResponseV2 { pub claims_and_scope_override_details: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ClaimsAndScopeOverrideDetailsV2` allows lambda to add, suppress or override claims in the token +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ClaimsAndScopeOverrideDetailsV2 { pub group_override_details: GroupConfiguration, pub id_token_generation: Option, pub access_token_generation: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoIdTokenGenerationV2` allows lambda to customize the ID Token before generation +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoIdTokenGenerationV2 { pub claims_to_add_or_override: HashMap, pub claims_to_suppress: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoAccessTokenGenerationV2` allows lambda to customize the Access Token before generation +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoAccessTokenGenerationV2 { @@ -336,9 +525,17 @@ pub struct CognitoAccessTokenGenerationV2 { pub claims_to_suppress: Vec, pub scopes_to_add: Vec, pub scopes_to_suppress: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPostAuthenticationRequest` contains the request portion of a PostAuthentication event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsPostAuthenticationRequest { @@ -349,12 +546,29 @@ pub struct CognitoEventUserPoolsPostAuthenticationRequest { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub client_metadata: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsPostAuthenticationResponse` contains the response portion of a PostAuthentication event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct CognitoEventUserPoolsPostAuthenticationResponse {} +pub struct CognitoEventUserPoolsPostAuthenticationResponse { + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, +} /// `CognitoEventUserPoolsMigrateUserRequest` contains the request portion of a MigrateUser event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsMigrateUserRequest { @@ -366,9 +580,17 @@ pub struct CognitoEventUserPoolsMigrateUserRequest { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub client_metadata: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsMigrateUserResponse` contains the response portion of a MigrateUser event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsMigrateUserResponse { @@ -383,9 +605,17 @@ pub struct CognitoEventUserPoolsMigrateUserResponse { pub desired_delivery_mediums: Option>, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub force_alias_creation: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ClaimsOverrideDetails` allows lambda to add, suppress or override claims in the token +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ClaimsOverrideDetails { @@ -394,19 +624,35 @@ pub struct ClaimsOverrideDetails { #[serde(default)] pub claims_to_add_or_override: HashMap, pub claims_to_suppress: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `GroupConfiguration` allows lambda to override groups, roles and set a preferred role +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct GroupConfiguration { pub groups_to_override: Vec, pub iam_roles_to_override: Vec, pub preferred_role: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsChallengeResult` represents a challenge that is presented to the user in the authentication /// process that is underway, along with the corresponding result. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsChallengeResult { @@ -415,9 +661,17 @@ pub struct CognitoEventUserPoolsChallengeResult { pub challenge_result: bool, #[serde(default)] pub challenge_metadata: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsDefineAuthChallengeRequest` defines auth challenge request parameters +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsDefineAuthChallengeRequest { @@ -430,9 +684,17 @@ pub struct CognitoEventUserPoolsDefineAuthChallengeRequest { pub client_metadata: HashMap, #[serde(default)] pub user_not_found: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsDefineAuthChallengeResponse` defines auth challenge response parameters +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsDefineAuthChallengeResponse { @@ -442,9 +704,17 @@ pub struct CognitoEventUserPoolsDefineAuthChallengeResponse { pub issue_tokens: bool, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub fail_authentication: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsDefineAuthChallenge` sent by AWS Cognito User Pools to initiate custom authentication flow +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsDefineAuthChallenge { @@ -454,8 +724,16 @@ pub struct CognitoEventUserPoolsDefineAuthChallenge { CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsDefineAuthChallengeRequest, pub response: CognitoEventUserPoolsDefineAuthChallengeResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsDefineAuthChallengeTriggerSource { #[serde(rename = "DefineAuthChallenge_Authentication")] @@ -464,6 +742,7 @@ pub enum CognitoEventUserPoolsDefineAuthChallengeTriggerSource { } /// `CognitoEventUserPoolsCreateAuthChallengeRequest` defines create auth challenge request parameters +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCreateAuthChallengeRequest { @@ -478,9 +757,17 @@ pub struct CognitoEventUserPoolsCreateAuthChallengeRequest { pub client_metadata: HashMap, #[serde(default)] pub user_not_found: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsCreateAuthChallengeResponse` defines create auth challenge response parameters +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCreateAuthChallengeResponse { @@ -492,9 +779,17 @@ pub struct CognitoEventUserPoolsCreateAuthChallengeResponse { pub private_challenge_parameters: HashMap, #[serde(default)] pub challenge_metadata: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsCreateAuthChallenge` sent by AWS Cognito User Pools to create a challenge to present to the user +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCreateAuthChallenge { @@ -504,8 +799,16 @@ pub struct CognitoEventUserPoolsCreateAuthChallenge { CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsCreateAuthChallengeRequest, pub response: CognitoEventUserPoolsCreateAuthChallengeResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsCreateAuthChallengeTriggerSource { #[serde(rename = "CreateAuthChallenge_Authentication")] @@ -514,6 +817,7 @@ pub enum CognitoEventUserPoolsCreateAuthChallengeTriggerSource { } /// `CognitoEventUserPoolsVerifyAuthChallengeRequest` defines verify auth challenge request parameters +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsVerifyAuthChallengeRequest @@ -534,18 +838,34 @@ where pub client_metadata: HashMap, #[serde(default)] pub user_not_found: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsVerifyAuthChallengeResponse` defines verify auth challenge response parameters +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsVerifyAuthChallengeResponse { #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub answer_correct: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsVerifyAuthChallenge` sent by AWS Cognito User Pools to verify if the response from the end user /// for a custom Auth Challenge is valid or not +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsVerifyAuthChallenge { @@ -555,8 +875,16 @@ pub struct CognitoEventUserPoolsVerifyAuthChallenge { CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsVerifyAuthChallengeRequest, pub response: CognitoEventUserPoolsVerifyAuthChallengeResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsVerifyAuthChallengeTriggerSource { #[serde(rename = "VerifyAuthChallengeResponse_Authentication")] @@ -566,6 +894,7 @@ pub enum CognitoEventUserPoolsVerifyAuthChallengeTriggerSource { /// `CognitoEventUserPoolsCustomMessage` is sent by AWS Cognito User Pools before a verification or MFA message is sent, /// allowing a user to customize the message dynamically. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCustomMessage { @@ -574,8 +903,16 @@ pub struct CognitoEventUserPoolsCustomMessage { pub cognito_event_user_pools_header: CognitoEventUserPoolsHeader, pub request: CognitoEventUserPoolsCustomMessageRequest, pub response: CognitoEventUserPoolsCustomMessageResponse, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)] pub enum CognitoEventUserPoolsCustomMessageTriggerSource { #[serde(rename = "CustomMessage_SignUp")] @@ -596,6 +933,7 @@ pub enum CognitoEventUserPoolsCustomMessageTriggerSource { } /// `CognitoEventUserPoolsCustomMessageRequest` contains the request portion of a CustomMessage event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCustomMessageRequest @@ -614,9 +952,17 @@ where #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub client_metadata: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `CognitoEventUserPoolsCustomMessageResponse` contains the response portion of a CustomMessage event +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CognitoEventUserPoolsCustomMessageResponse { @@ -626,6 +972,13 @@ pub struct CognitoEventUserPoolsCustomMessageResponse { pub email_message: Option, #[serde(default)] pub email_subject: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] @@ -855,7 +1208,8 @@ mod test { assert!(parsed.request.user_not_found); - let output: String = serde_json::to_string(&parsed).unwrap(); + let output: String = serde_json::to_string_pretty(&parsed).unwrap(); + println!("output is: {output}"); let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); assert_eq!(parsed, reparsed); } diff --git a/lambda-events/src/event/config/mod.rs b/lambda-events/src/event/config/mod.rs index 7c06e13b2..dda9afa3d 100644 --- a/lambda-events/src/event/config/mod.rs +++ b/lambda-events/src/event/config/mod.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `ConfigEvent` contains data from an event sent from AWS Config -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConfigEvent { /// The ID of the AWS account that owns the rule @@ -34,6 +37,13 @@ pub struct ConfigEvent { pub rule_parameters: Option, #[serde(default)] pub version: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/connect/mod.rs b/lambda-events/src/event/connect/mod.rs index 04f26eb5b..b5f5fe3df 100644 --- a/lambda-events/src/event/connect/mod.rs +++ b/lambda-events/src/event/connect/mod.rs @@ -1,10 +1,13 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; /// `ConnectEvent` contains the data structure for a Connect event. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConnectEvent { #[serde(rename = "Details")] @@ -13,10 +16,18 @@ pub struct ConnectEvent { #[serde(default)] #[serde(rename = "Name")] pub name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ConnectDetails` holds the details of a Connect event -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConnectDetails { #[serde(rename = "ContactData")] @@ -26,10 +37,18 @@ pub struct ConnectDetails { #[serde(default)] #[serde(rename = "Parameters")] pub parameters: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ConnectContactData` holds all of the contact information for the user that invoked the Connect event. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConnectContactData { /// The custom attributes from Connect that the Lambda function was invoked with. @@ -62,10 +81,18 @@ pub struct ConnectContactData { #[serde(default)] #[serde(rename = "InstanceARN")] pub instance_arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ConnectEndpoint` represents routing information. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConnectEndpoint { #[serde(default)] @@ -74,10 +101,18 @@ pub struct ConnectEndpoint { #[serde(default)] #[serde(rename = "Type")] pub type_: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ConnectQueue` represents a queue object. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ConnectQueue { #[serde(default)] @@ -86,6 +121,13 @@ pub struct ConnectQueue { #[serde(default)] #[serde(rename = "ARN")] pub arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type ConnectResponse = HashMap; diff --git a/lambda-events/src/event/documentdb/events/commom_types.rs b/lambda-events/src/event/documentdb/events/commom_types.rs index 5d1bdc196..9624901de 100644 --- a/lambda-events/src/event/documentdb/events/commom_types.rs +++ b/lambda-events/src/event/documentdb/events/commom_types.rs @@ -5,40 +5,88 @@ use serde_json::Value; pub type AnyDocument = HashMap; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DatabaseCollection { db: String, #[serde(default)] coll: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct DocumentId { #[serde(rename = "_data")] pub data: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct DocumentKeyIdOid { #[serde(rename = "$oid")] pub oid: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct DocumentKeyId { #[serde(rename = "_id")] pub id: DocumentKeyIdOid, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct InnerTimestamp { t: usize, i: usize, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct Timestamp { #[serde(rename = "$timestamp")] pub timestamp: InnerTimestamp, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/delete_event.rs b/lambda-events/src/event/documentdb/events/delete_event.rs index 7761d62f8..1c2e6afea 100644 --- a/lambda-events/src/event/documentdb/events/delete_event.rs +++ b/lambda-events/src/event/documentdb/events/delete_event.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, DocumentKeyId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeDeleteEvent { @@ -17,4 +20,11 @@ pub struct ChangeDeleteEvent { // operation_type: String, #[serde(default)] txn_number: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/drop_database_event.rs b/lambda-events/src/event/documentdb/events/drop_database_event.rs index 273c897c6..2506f9cba 100644 --- a/lambda-events/src/event/documentdb/events/drop_database_event.rs +++ b/lambda-events/src/event/documentdb/events/drop_database_event.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeDropDatabaseEvent { @@ -16,4 +19,11 @@ pub struct ChangeDropDatabaseEvent { // operation_type: String, #[serde(default)] txn_number: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/drop_event.rs b/lambda-events/src/event/documentdb/events/drop_event.rs index a6f929342..42bb566b9 100644 --- a/lambda-events/src/event/documentdb/events/drop_event.rs +++ b/lambda-events/src/event/documentdb/events/drop_event.rs @@ -1,6 +1,9 @@ use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, Timestamp}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeDropEvent { @@ -15,4 +18,11 @@ pub struct ChangeDropEvent { // operation_type: String, #[serde(default)] txn_number: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/insert_event.rs b/lambda-events/src/event/documentdb/events/insert_event.rs index 2f4df3972..0c1392208 100644 --- a/lambda-events/src/event/documentdb/events/insert_event.rs +++ b/lambda-events/src/event/documentdb/events/insert_event.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, DocumentKeyId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeInsertEvent { @@ -17,4 +20,11 @@ pub struct ChangeInsertEvent { //operation_type: String, #[serde(default)] txn_number: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/invalidate_event.rs b/lambda-events/src/event/documentdb/events/invalidate_event.rs index 47469ff98..f721013ee 100644 --- a/lambda-events/src/event/documentdb/events/invalidate_event.rs +++ b/lambda-events/src/event/documentdb/events/invalidate_event.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{DocumentId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeInvalidateEvent { @@ -10,4 +13,11 @@ pub struct ChangeInvalidateEvent { #[serde(default)] cluster_time: Option, // operation_type: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/rename_event.rs b/lambda-events/src/event/documentdb/events/rename_event.rs index 8bc250fb6..75797aca5 100644 --- a/lambda-events/src/event/documentdb/events/rename_event.rs +++ b/lambda-events/src/event/documentdb/events/rename_event.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeRenameEvent { @@ -18,4 +21,11 @@ pub struct ChangeRenameEvent { #[serde(default)] txn_number: Option, to: DatabaseCollection, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/replace_event.rs b/lambda-events/src/event/documentdb/events/replace_event.rs index c253e2726..0ff6ffba8 100644 --- a/lambda-events/src/event/documentdb/events/replace_event.rs +++ b/lambda-events/src/event/documentdb/events/replace_event.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, DocumentKeyId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeReplaceEvent { @@ -17,4 +20,11 @@ pub struct ChangeReplaceEvent { // operation_type: String, #[serde(default)] txn_number: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/events/update_event.rs b/lambda-events/src/event/documentdb/events/update_event.rs index 04369cf0a..8f6a48a9f 100644 --- a/lambda-events/src/event/documentdb/events/update_event.rs +++ b/lambda-events/src/event/documentdb/events/update_event.rs @@ -1,22 +1,41 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use super::commom_types::{AnyDocument, DatabaseCollection, DocumentId, DocumentKeyId, Timestamp}; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UpdateTruncate { field: String, new_size: usize, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UpdateDescription { removed_fields: Vec, truncated_arrays: Vec, updated_fields: AnyDocument, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChangeUpdateEvent { @@ -33,4 +52,11 @@ pub struct ChangeUpdateEvent { update_description: UpdateDescription, #[serde(default)] txn_number: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/documentdb/mod.rs b/lambda-events/src/event/documentdb/mod.rs index 67f7c9ad6..c802db628 100644 --- a/lambda-events/src/event/documentdb/mod.rs +++ b/lambda-events/src/event/documentdb/mod.rs @@ -6,7 +6,10 @@ use self::events::{ replace_event::ChangeReplaceEvent, update_event::ChangeUpdateEvent, }; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(tag = "operationType", rename_all = "camelCase")] pub enum ChangeEvent { @@ -20,11 +23,20 @@ pub enum ChangeEvent { Rename(ChangeRenameEvent), } +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct DocumentDbInnerEvent { pub event: ChangeEvent, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DocumentDbEvent { @@ -33,6 +45,13 @@ pub struct DocumentDbEvent { pub events: Vec, #[serde(default)] pub event_source: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/dynamodb/attributes.rs b/lambda-events/src/event/dynamodb/attributes.rs index e1a42c836..ac43adea4 100644 --- a/lambda-events/src/event/dynamodb/attributes.rs +++ b/lambda-events/src/event/dynamodb/attributes.rs @@ -15,7 +15,7 @@ mod test { let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); match attr { AttributeValue::Null(true) => {} - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -31,7 +31,7 @@ mod test { let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); match attr { AttributeValue::S(ref s) => assert_eq!("value", s.as_str()), - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -47,7 +47,7 @@ mod test { let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); match attr { AttributeValue::N(ref n) => assert_eq!("123.45", n.as_str()), - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -68,7 +68,7 @@ mod test { .unwrap(); assert_eq!(&expected, b) } - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -84,7 +84,7 @@ mod test { let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); match attr { AttributeValue::Bool(b) => assert!(b), - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -103,7 +103,7 @@ mod test { let expected = vec!["Giraffe", "Hippo", "Zebra"]; assert_eq!(expected, s.iter().collect::>()); } - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -122,7 +122,7 @@ mod test { let expected = vec!["42.2", "-19", "7.5", "3.14"]; assert_eq!(expected, s.iter().collect::>()); } - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -144,7 +144,7 @@ mod test { .collect::>(); assert_eq!(&expected, s); } - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -167,7 +167,7 @@ mod test { ]; assert_eq!(&expected, s); } - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } let reparsed = serde_json::to_value(attr).unwrap(); @@ -188,7 +188,7 @@ mod test { expected.insert("Age".into(), AttributeValue::N("35".into())); assert_eq!(expected, s); } - other => panic!("unexpected value {:?}", other), + other => panic!("unexpected value {other:?}"), } } } diff --git a/lambda-events/src/event/dynamodb/mod.rs b/lambda-events/src/event/dynamodb/mod.rs index 2a3d75582..62872f3e1 100644 --- a/lambda-events/src/event/dynamodb/mod.rs +++ b/lambda-events/src/event/dynamodb/mod.rs @@ -5,17 +5,21 @@ use crate::{ }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::fmt; #[cfg(test)] mod attributes; -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum StreamViewType { NewImage, OldImage, NewAndOldImages, + #[default] KeysOnly, } @@ -27,16 +31,18 @@ impl fmt::Display for StreamViewType { StreamViewType::NewAndOldImages => "NEW_AND_OLD_IMAGES", StreamViewType::KeysOnly => "KEYS_ONLY", }; - write!(f, "{}", val) + write!(f, "{val}") } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum StreamStatus { Enabling, Enabled, Disabling, + #[default] Disabled, } @@ -48,14 +54,16 @@ impl fmt::Display for StreamStatus { StreamStatus::Disabling => "DISABLING", StreamStatus::Disabled => "DISABLED", }; - write!(f, "{}", val) + write!(f, "{val}") } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum SharedIteratorType { TrimHorizon, + #[default] Latest, AtSequenceNumber, AfterSequenceNumber, @@ -69,13 +77,15 @@ impl fmt::Display for SharedIteratorType { SharedIteratorType::AtSequenceNumber => "AT_SEQUENCE_NUMBER", SharedIteratorType::AfterSequenceNumber => "AFTER_SEQUENCE_NUMBER", }; - write!(f, "{}", val) + write!(f, "{val}") } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum OperationType { + #[default] Insert, Modify, Remove, @@ -88,13 +98,15 @@ impl fmt::Display for OperationType { OperationType::Modify => "MODIFY", OperationType::Remove => "REMOVE", }; - write!(f, "{}", val) + write!(f, "{val}") } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum KeyType { + #[default] Hash, Range, } @@ -105,21 +117,30 @@ impl fmt::Display for KeyType { KeyType::Hash => "HASH", KeyType::Range => "RANGE", }; - write!(f, "{}", val) + write!(f, "{val}") } } /// The `Event` stream event handled to Lambda -/// http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-ddb-update -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +/// +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct Event { #[serde(rename = "Records")] pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `TimeWindowEvent` represents an Amazon Dynamodb event when using time windows -/// ref. https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-windows -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +/// ref. +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TimeWindowEvent { #[serde(rename = "DynamoDBEvent")] @@ -128,9 +149,17 @@ pub struct TimeWindowEvent { #[serde(rename = "TimeWindowProperties")] #[serde(flatten)] pub time_window_properties: TimeWindowProperties, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `TimeWindowEventResponse` is the outer structure to report batch item failures for DynamoDBTimeWindowEvent. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TimeWindowEventResponse { @@ -138,10 +167,18 @@ pub struct TimeWindowEventResponse { #[serde(flatten)] pub time_window_event_response_properties: TimeWindowEventResponseProperties, pub batch_item_failures: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// EventRecord stores information about each record of a DynamoDb stream event -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EventRecord { /// The region in which the GetRecords request was received. @@ -198,8 +235,16 @@ pub struct EventRecord { /// The DynamoDB table that this event was recorded for. #[serde(default)] pub table_name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UserIdentity { @@ -207,16 +252,24 @@ pub struct UserIdentity { pub type_: String, #[serde(default)] pub principal_id: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `DynamoDbStreamRecord` represents a description of a single data modification that was performed on an item /// in a DynamoDB table. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StreamRecord { /// The approximate date and time when the stream record was created, in UNIX - /// epoch time (http://www.epochconverter.com/) format. Might not be present in - /// the record: https://github.com/awslabs/aws-lambda-rust-runtime/issues/889 + /// epoch time () format. Might not be present in + /// the record: #[serde(rename = "ApproximateCreationDateTime")] #[serde(with = "float_unix_epoch")] #[serde(default)] @@ -248,6 +301,13 @@ pub struct StreamRecord { #[serde(default)] #[serde(rename = "StreamViewType")] pub stream_view_type: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/ecr_scan/mod.rs b/lambda-events/src/event/ecr_scan/mod.rs index 5502e81ac..b28678f42 100644 --- a/lambda-events/src/event/ecr_scan/mod.rs +++ b/lambda-events/src/event/ecr_scan/mod.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct EcrScanEvent { #[serde(default)] @@ -20,9 +23,17 @@ pub struct EcrScanEvent { #[serde(default)] pub account: Option, pub detail: EcrScanEventDetailType, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct EcrScanEventDetailType { #[serde(default)] @@ -38,9 +49,17 @@ pub struct EcrScanEventDetailType { pub image_digest: Option, #[serde(rename = "image-tags")] pub image_tags: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct EcrScanEventFindingSeverityCounts { #[serde(default)] @@ -61,6 +80,13 @@ pub struct EcrScanEventFindingSeverityCounts { #[serde(default)] #[serde(rename = "UNDEFINED")] pub undefined: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/eventbridge/mod.rs b/lambda-events/src/event/eventbridge/mod.rs index 7756e0e41..824de1ef4 100644 --- a/lambda-events/src/event/eventbridge/mod.rs +++ b/lambda-events/src/event/eventbridge/mod.rs @@ -5,7 +5,8 @@ use serde_json::Value; /// Parse EventBridge events. /// Deserialize the event detail into a structure that's `DeserializeOwned`. /// -/// See https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events-structure.html for structure details. +/// See for structure details. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(bound(deserialize = "T1: DeserializeOwned"))] #[serde(rename_all = "kebab-case")] @@ -30,6 +31,13 @@ where pub resources: Option>, #[serde(bound = "")] pub detail: T1, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/firehose/mod.rs b/lambda-events/src/event/firehose/mod.rs index 6a0a13fd0..d0dec03db 100644 --- a/lambda-events/src/event/firehose/mod.rs +++ b/lambda-events/src/event/firehose/mod.rs @@ -3,10 +3,13 @@ use crate::{ encodings::{Base64Data, MillisecondTimestamp}, }; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; /// `KinesisFirehoseEvent` represents the input event from Amazon Kinesis Firehose. It is used as the input parameter. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisFirehoseEvent { #[serde(default)] @@ -20,9 +23,17 @@ pub struct KinesisFirehoseEvent { #[serde(default)] pub region: Option, pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisFirehoseEventRecord { #[serde(default)] @@ -31,15 +42,31 @@ pub struct KinesisFirehoseEventRecord { pub data: Base64Data, #[serde(rename = "kinesisRecordMetadata")] pub kinesis_firehose_record_metadata: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisFirehoseResponse { pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisFirehoseResponseRecord { #[serde(default)] @@ -49,17 +76,33 @@ pub struct KinesisFirehoseResponseRecord { pub result: Option, pub data: Base64Data, pub metadata: KinesisFirehoseResponseRecordMetadata, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisFirehoseResponseRecordMetadata { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub partition_keys: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisFirehoseRecordMetadata { #[serde(default)] @@ -70,6 +113,13 @@ pub struct KinesisFirehoseRecordMetadata { pub sequence_number: Option, pub subsequence_number: i64, pub approximate_arrival_timestamp: MillisecondTimestamp, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/iam/mod.rs b/lambda-events/src/event/iam/mod.rs index 36f59c7b1..fd190950b 100644 --- a/lambda-events/src/event/iam/mod.rs +++ b/lambda-events/src/event/iam/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::{borrow::Cow, collections::HashMap, fmt}; use serde::{ @@ -6,15 +8,24 @@ use serde::{ }; /// `IamPolicyDocument` represents an IAM policy document. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "PascalCase")] pub struct IamPolicyDocument { #[serde(default)] pub version: Option, pub statement: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `IamPolicyStatement` represents one statement from IAM policy with action, effect and resource +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct IamPolicyStatement { @@ -27,10 +38,18 @@ pub struct IamPolicyStatement { #[serde(default, deserialize_with = "deserialize_policy_condition")] #[serde(skip_serializing_if = "Option::is_none")] pub condition: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type IamPolicyCondition = HashMap>>; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub enum IamPolicyEffect { #[default] @@ -178,6 +197,8 @@ mod tests { effect: IamPolicyEffect::Allow, resource: vec!["some:resource".into()], condition: None, + #[cfg(feature = "catch-all-fields")] + other: Default::default(), }; let policy_ser = serde_json::to_value(policy).unwrap(); diff --git a/lambda-events/src/event/iot/mod.rs b/lambda-events/src/event/iot/mod.rs index 3835b515b..e1d7b5046 100644 --- a/lambda-events/src/event/iot/mod.rs +++ b/lambda-events/src/event/iot/mod.rs @@ -1,10 +1,13 @@ use crate::{custom_serde::serialize_headers, encodings::Base64Data, iam::IamPolicyDocument}; use http::HeaderMap; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `IoTCoreCustomAuthorizerRequest` represents the request to an IoT Core custom authorizer. -/// See https://docs.aws.amazon.com/iot/latest/developerguide/config-custom-auth.html -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +/// See +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreCustomAuthorizerRequest { #[serde(default)] @@ -13,24 +16,48 @@ pub struct IoTCoreCustomAuthorizerRequest { pub protocols: Vec, pub protocol_data: Option, pub connection_metadata: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreProtocolData { pub tls: Option, pub http: Option, pub mqtt: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreTlsContext { #[serde(default)] pub server_name: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreHttpContext { #[serde(deserialize_with = "http_serde::header_map::deserialize", default)] @@ -38,9 +65,17 @@ pub struct IoTCoreHttpContext { pub headers: HeaderMap, #[serde(default)] pub query_string: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreMqttContext { #[serde(default)] @@ -48,18 +83,34 @@ pub struct IoTCoreMqttContext { pub password: Base64Data, #[serde(default)] pub username: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreConnectionMetadata { #[serde(default)] pub id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `IoTCoreCustomAuthorizerResponse` represents the response from an IoT Core custom authorizer. -/// See https://docs.aws.amazon.com/iot/latest/developerguide/config-custom-auth.html -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +/// See +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCoreCustomAuthorizerResponse { pub is_authenticated: bool, @@ -68,6 +119,13 @@ pub struct IoTCoreCustomAuthorizerResponse { pub disconnect_after_in_seconds: u32, pub refresh_after_in_seconds: u32, pub policy_documents: Vec>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/iot_1_click/mod.rs b/lambda-events/src/event/iot_1_click/mod.rs index bf010b507..a88041c0e 100644 --- a/lambda-events/src/event/iot_1_click/mod.rs +++ b/lambda-events/src/event/iot_1_click/mod.rs @@ -1,34 +1,61 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; /// `IoTOneClickEvent` represents a click event published by clicking button type /// device. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTOneClickEvent { pub device_event: IoTOneClickDeviceEvent, pub device_info: IoTOneClickDeviceInfo, pub placement_info: IoTOneClickPlacementInfo, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTOneClickDeviceEvent { pub button_clicked: IoTOneClickButtonClicked, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTOneClickButtonClicked { #[serde(default)] pub click_type: Option, #[serde(default)] pub reported_time: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTOneClickDeviceInfo { #[serde(deserialize_with = "deserialize_lambda_map")] @@ -39,9 +66,17 @@ pub struct IoTOneClickDeviceInfo { #[serde(default)] pub device_id: Option, pub remaining_life: f64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTOneClickPlacementInfo { #[serde(default)] @@ -54,6 +89,13 @@ pub struct IoTOneClickPlacementInfo { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub devices: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/iot_button/mod.rs b/lambda-events/src/event/iot_button/mod.rs index 2d2e4627d..70a24f9d3 100644 --- a/lambda-events/src/event/iot_button/mod.rs +++ b/lambda-events/src/event/iot_button/mod.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTButtonEvent { #[serde(default)] @@ -9,6 +12,13 @@ pub struct IoTButtonEvent { pub click_type: Option, #[serde(default)] pub battery_voltage: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/iot_deprecated/mod.rs b/lambda-events/src/event/iot_deprecated/mod.rs index 12c1df990..22b13db9a 100644 --- a/lambda-events/src/event/iot_deprecated/mod.rs +++ b/lambda-events/src/event/iot_deprecated/mod.rs @@ -1,9 +1,12 @@ use crate::iot::*; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `IoTCustomAuthorizerRequest` contains data coming in to a custom IoT device gateway authorizer function. /// Deprecated: Use IoTCoreCustomAuthorizerRequest instead. `IoTCustomAuthorizerRequest` does not correctly model the request schema -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCustomAuthorizerRequest { pub http_context: Option, @@ -14,6 +17,13 @@ pub struct IoTCustomAuthorizerRequest { pub authorization_token: Option, #[serde(default)] pub token_signature: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type IoTHttpContext = IoTCoreHttpContext; @@ -24,7 +34,8 @@ pub type IoTTlsContext = IoTCoreTlsContext; /// `IoTCustomAuthorizerResponse` represents the expected format of an IoT device gateway authorization response. /// Deprecated: Use IoTCoreCustomAuthorizerResponse. `IoTCustomAuthorizerResponse` does not correctly model the response schema. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct IoTCustomAuthorizerResponse { pub is_authenticated: bool, @@ -33,4 +44,11 @@ pub struct IoTCustomAuthorizerResponse { pub disconnect_after_in_seconds: i32, pub refresh_after_in_seconds: i32, pub policy_documents: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/kafka/mod.rs b/lambda-events/src/event/kafka/mod.rs index 27a1e9218..ecd02d873 100644 --- a/lambda-events/src/event/kafka/mod.rs +++ b/lambda-events/src/event/kafka/mod.rs @@ -1,8 +1,11 @@ use crate::{custom_serde::deserialize_lambda_map, encodings::MillisecondTimestamp}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KafkaEvent { #[serde(default)] @@ -14,9 +17,17 @@ pub struct KafkaEvent { pub records: HashMap>, #[serde(default)] pub bootstrap_servers: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KafkaRecord { #[serde(default)] @@ -29,6 +40,13 @@ pub struct KafkaRecord { pub key: Option, pub value: Option, pub headers: Vec>>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/kinesis/analytics.rs b/lambda-events/src/event/kinesis/analytics.rs index 74c956068..9ca62f690 100644 --- a/lambda-events/src/event/kinesis/analytics.rs +++ b/lambda-events/src/event/kinesis/analytics.rs @@ -1,6 +1,9 @@ use crate::encodings::Base64Data; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisAnalyticsOutputDeliveryEvent { @@ -9,22 +12,46 @@ pub struct KinesisAnalyticsOutputDeliveryEvent { #[serde(default)] pub application_arn: Option, pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisAnalyticsOutputDeliveryEventRecord { #[serde(default)] pub record_id: Option, pub data: Base64Data, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisAnalyticsOutputDeliveryResponse { pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisAnalyticsOutputDeliveryResponseRecord { @@ -33,4 +60,11 @@ pub struct KinesisAnalyticsOutputDeliveryResponseRecord { /// possible values include Ok and DeliveryFailed #[serde(default)] pub result: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/kinesis/event.rs b/lambda-events/src/event/kinesis/event.rs index fac80e07d..215e9288b 100644 --- a/lambda-events/src/event/kinesis/event.rs +++ b/lambda-events/src/event/kinesis/event.rs @@ -3,17 +3,28 @@ use crate::{ time_window::{TimeWindowEventResponseProperties, TimeWindowProperties}, }; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Default, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisEvent { #[serde(rename = "Records")] pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `KinesisTimeWindowEvent` represents an Amazon Dynamodb event when using time windows -/// ref. https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-windows -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +/// ref. +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisTimeWindowEvent { #[serde(rename = "KinesisEvent")] @@ -22,9 +33,17 @@ pub struct KinesisTimeWindowEvent { #[serde(rename = "TimeWindowProperties")] #[serde(flatten)] pub time_window_properties: TimeWindowProperties, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `KinesisTimeWindowEventResponse` is the outer structure to report batch item failures for KinesisTimeWindowEvent. +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisTimeWindowEventResponse { @@ -32,9 +51,17 @@ pub struct KinesisTimeWindowEventResponse { #[serde(flatten)] pub time_window_event_response_properties: TimeWindowEventResponseProperties, // pub batch_item_failures: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisEventRecord { /// nolint: stylecheck @@ -57,9 +84,17 @@ pub struct KinesisEventRecord { #[serde(default)] pub invoke_identity_arn: Option, pub kinesis: KinesisRecord, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisRecord { pub approximate_arrival_timestamp: SecondTimestamp, @@ -72,8 +107,16 @@ pub struct KinesisRecord { pub sequence_number: String, #[serde(default)] pub kinesis_schema_version: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum KinesisEncryptionType { @@ -109,4 +152,17 @@ mod test { let reparsed: KinesisEvent = serde_json::from_slice(output.as_bytes()).unwrap(); assert_eq!(parsed, reparsed); } + + /// `cargo lambda init` autogenerates code that relies on `Default` being implemented for event structs. + /// + /// This test validates that `Default` is implemented for each KinesisEvent struct. + #[test] + #[cfg(feature = "kinesis")] + fn test_ensure_default_implemented_for_structs() { + let _kinesis_event = KinesisEvent::default(); + let _kinesis_time_window_event = KinesisTimeWindowEvent::default(); + let _kinesis_event_record = KinesisEventRecord::default(); + let _kinesis_record = KinesisRecord::default(); + let _kinesis_encryption_type = KinesisEncryptionType::default(); + } } diff --git a/lambda-events/src/event/lambda_function_urls/mod.rs b/lambda-events/src/event/lambda_function_urls/mod.rs index 37ddfe39b..f57716237 100644 --- a/lambda-events/src/event/lambda_function_urls/mod.rs +++ b/lambda-events/src/event/lambda_function_urls/mod.rs @@ -1,11 +1,14 @@ use http::HeaderMap; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::{deserialize_lambda_map, serialize_headers}; /// `LambdaFunctionUrlRequest` contains data coming from the HTTP request to a Lambda Function URL. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LambdaFunctionUrlRequest { /// Version is expected to be `"2.0"` @@ -25,10 +28,18 @@ pub struct LambdaFunctionUrlRequest { pub request_context: LambdaFunctionUrlRequestContext, pub body: Option, pub is_base64_encoded: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `LambdaFunctionUrlRequestContext` contains the information to identify the AWS account and resources invoking the Lambda function. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LambdaFunctionUrlRequestContext { #[serde(default)] @@ -50,17 +61,33 @@ pub struct LambdaFunctionUrlRequestContext { pub time: Option, pub time_epoch: i64, pub http: LambdaFunctionUrlRequestContextHttpDescription, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `LambdaFunctionUrlRequestContextAuthorizerDescription` contains authorizer information for the request context. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LambdaFunctionUrlRequestContextAuthorizerDescription { pub iam: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `LambdaFunctionUrlRequestContextAuthorizerIamDescription` contains IAM information for the request context. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LambdaFunctionUrlRequestContextAuthorizerIamDescription { #[serde(default)] @@ -73,10 +100,18 @@ pub struct LambdaFunctionUrlRequestContextAuthorizerIamDescription { pub user_arn: Option, #[serde(default)] pub user_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `LambdaFunctionUrlRequestContextHttpDescription` contains HTTP information for the request context. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LambdaFunctionUrlRequestContextHttpDescription { #[serde(default)] @@ -89,10 +124,18 @@ pub struct LambdaFunctionUrlRequestContextHttpDescription { pub source_ip: Option, #[serde(default)] pub user_agent: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `LambdaFunctionUrlResponse` configures the HTTP response to be returned by Lambda Function URL for the request. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LambdaFunctionUrlResponse { pub status_code: i64, @@ -103,4 +146,11 @@ pub struct LambdaFunctionUrlResponse { pub body: Option, pub is_base64_encoded: bool, pub cookies: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/lex/mod.rs b/lambda-events/src/event/lex/mod.rs index d8f9403cb..b64279d72 100644 --- a/lambda-events/src/event/lex/mod.rs +++ b/lambda-events/src/event/lex/mod.rs @@ -1,9 +1,12 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexEvent { pub message_version: Option, @@ -20,17 +23,33 @@ pub struct LexEvent { pub alternative_intents: Option>, /// Deprecated: the DialogAction field is never populated by Lex events pub dialog_action: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexBot { pub name: Option, pub alias: Option, pub version: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexCurrentIntent { pub name: Option, @@ -40,9 +59,17 @@ pub struct LexCurrentIntent { #[serde(default)] pub slot_details: HashMap, pub confirmation_status: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexAlternativeIntents { pub name: Option, @@ -52,16 +79,32 @@ pub struct LexAlternativeIntents { #[serde(default)] pub slot_details: HashMap, pub confirmation_status: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SlotDetail { pub resolutions: Option>>, pub original_value: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexDialogAction { pub type_: Option, @@ -73,28 +116,52 @@ pub struct LexDialogAction { pub slots: Option, pub slot_to_elicit: Option, pub response_card: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type SessionAttributes = HashMap; pub type Slots = HashMap>; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexResponse { pub session_attributes: SessionAttributes, pub dialog_action: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct LexResponseCard { pub version: Option, pub content_type: Option, pub generic_attachments: Option>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Attachment { pub title: Option, @@ -102,6 +169,13 @@ pub struct Attachment { pub image_url: Option, pub attachment_link_url: Option, pub buttons: Option>>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/mod.rs b/lambda-events/src/event/mod.rs index d63acc4d4..275450fdd 100644 --- a/lambda-events/src/event/mod.rs +++ b/lambda-events/src/event/mod.rs @@ -1,162 +1,203 @@ /// AWS Lambda event definitions for activemq. #[cfg(feature = "activemq")] +#[cfg_attr(docsrs, doc(cfg(feature = "activemq")))] pub mod activemq; /// AWS Lambda event definitions for alb. #[cfg(feature = "alb")] +#[cfg_attr(docsrs, doc(cfg(feature = "alb")))] pub mod alb; /// AWS Lambda event definitions for apigw. #[cfg(feature = "apigw")] +#[cfg_attr(docsrs, doc(cfg(feature = "apigw")))] pub mod apigw; /// AWS Lambda event definitions for appsync. #[cfg(feature = "appsync")] +#[cfg_attr(docsrs, doc(cfg(feature = "appsync")))] pub mod appsync; /// AWS Lambda event definitions for autoscaling. #[cfg(feature = "autoscaling")] +#[cfg_attr(docsrs, doc(cfg(feature = "autoscaling")))] pub mod autoscaling; /// AWS Lambda event definitions for agent for amazon bedrock #[cfg(feature = "bedrock_agent_runtime")] +#[cfg_attr(docsrs, doc(cfg(feature = "bedrock_agent_runtime")))] pub mod bedrock_agent_runtime; /// AWS Lambda event definitions for chime_bot. #[cfg(feature = "chime_bot")] +#[cfg_attr(docsrs, doc(cfg(feature = "chime_bot")))] pub mod chime_bot; /// AWS Lambda event definitions for clientvpn. #[cfg(feature = "clientvpn")] +#[cfg_attr(docsrs, doc(cfg(feature = "clientvpn")))] pub mod clientvpn; /// AWS Lambda event definitions for cloudformation. #[cfg(feature = "cloudformation")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudformation")))] pub mod cloudformation; /// AWS Lambda event definitions for CloudWatch alarms. #[cfg(feature = "cloudwatch_alarms")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudwatch_alarms")))] pub mod cloudwatch_alarms; /// AWS Lambda event definitions for CloudWatch events. #[cfg(feature = "cloudwatch_events")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudwatch_events")))] pub mod cloudwatch_events; /// AWS Lambda event definitions for cloudwatch_logs. #[cfg(feature = "cloudwatch_logs")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudwatch_logs")))] pub mod cloudwatch_logs; /// AWS Lambda event definitions for code_commit. #[cfg(feature = "code_commit")] +#[cfg_attr(docsrs, doc(cfg(feature = "code_commit")))] pub mod code_commit; /// AWS Lambda event definitions for codebuild. #[cfg(feature = "codebuild")] +#[cfg_attr(docsrs, doc(cfg(feature = "codebuild")))] pub mod codebuild; /// AWS Lambda event definitions for codedeploy. #[cfg(feature = "codedeploy")] +#[cfg_attr(docsrs, doc(cfg(feature = "codedeploy")))] pub mod codedeploy; /// AWS Lambda event definitions for codepipeline_cloudwatch. #[cfg(feature = "codepipeline_cloudwatch")] +#[cfg_attr(docsrs, doc(cfg(feature = "codepipeline_cloudwatch")))] pub mod codepipeline_cloudwatch; /// AWS Lambda event definitions for codepipeline_job. #[cfg(feature = "codepipeline_job")] +#[cfg_attr(docsrs, doc(cfg(feature = "codepipeline_job")))] pub mod codepipeline_job; /// AWS Lambda event definitions for cognito. #[cfg(feature = "cognito")] +#[cfg_attr(docsrs, doc(cfg(feature = "cognito")))] pub mod cognito; /// AWS Lambda event definitions for config. #[cfg(feature = "config")] +#[cfg_attr(docsrs, doc(cfg(feature = "config")))] pub mod config; /// AWS Lambda event definitions for connect. #[cfg(feature = "connect")] +#[cfg_attr(docsrs, doc(cfg(feature = "connect")))] pub mod connect; /// AWS Lambda event definitions for dynamodb. #[cfg(feature = "dynamodb")] +#[cfg_attr(docsrs, doc(cfg(feature = "dynamodb")))] pub mod dynamodb; /// AWS Lambda event definitions for ecr_scan. #[cfg(feature = "ecr_scan")] +#[cfg_attr(docsrs, doc(cfg(feature = "ecr_scan")))] pub mod ecr_scan; /// AWS Lambda event definitions for firehose. #[cfg(feature = "firehose")] +#[cfg_attr(docsrs, doc(cfg(feature = "firehose")))] pub mod firehose; /// AWS Lambda event definitions for iam. #[cfg(feature = "iam")] +#[cfg_attr(docsrs, doc(cfg(feature = "iam")))] pub mod iam; /// AWS Lambda event definitions for iot. #[cfg(feature = "iot")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot")))] pub mod iot; /// AWS Lambda event definitions for iot_1_click. #[cfg(feature = "iot_1_click")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot_1_click")))] pub mod iot_1_click; /// AWS Lambda event definitions for iot_button. #[cfg(feature = "iot_button")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot_button")))] pub mod iot_button; /// AWS Lambda event definitions for iot_deprecated. #[cfg(feature = "iot_deprecated")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot_deprecated")))] pub mod iot_deprecated; /// AWS Lambda event definitions for kafka. #[cfg(feature = "kafka")] +#[cfg_attr(docsrs, doc(cfg(feature = "kafka")))] pub mod kafka; /// AWS Lambda event definitions for kinesis. #[cfg(feature = "kinesis")] +#[cfg_attr(docsrs, doc(cfg(feature = "kinesis")))] pub mod kinesis; /// AWS Lambda event definitions for lambda_function_urls. #[cfg(feature = "lambda_function_urls")] +#[cfg_attr(docsrs, doc(cfg(feature = "lambda_function_urls")))] pub mod lambda_function_urls; /// AWS Lambda event definitions for lex. #[cfg(feature = "lex")] +#[cfg_attr(docsrs, doc(cfg(feature = "lex")))] pub mod lex; /// AWS Lambda event definitions for rabbitmq. #[cfg(feature = "rabbitmq")] +#[cfg_attr(docsrs, doc(cfg(feature = "rabbitmq")))] pub mod rabbitmq; /// AWS Lambda event definitions for s3. #[cfg(feature = "s3")] +#[cfg_attr(docsrs, doc(cfg(feature = "s3")))] pub mod s3; /// AWS Lambda event definitions for secretsmanager. #[cfg(feature = "secretsmanager")] +#[cfg_attr(docsrs, doc(cfg(feature = "secretsmanager")))] pub mod secretsmanager; /// AWS Lambda event definitions for ses. #[cfg(feature = "ses")] +#[cfg_attr(docsrs, doc(cfg(feature = "ses")))] pub mod ses; /// AWS Lambda event definitions for SNS. #[cfg(feature = "sns")] +#[cfg_attr(docsrs, doc(cfg(feature = "sns")))] pub mod sns; /// AWS Lambda event definitions for SQS. #[cfg(feature = "sqs")] +#[cfg_attr(docsrs, doc(cfg(feature = "sqs")))] pub mod sqs; /// AWS Lambda event definitions for streams. #[cfg(feature = "streams")] +#[cfg_attr(docsrs, doc(cfg(feature = "streams")))] pub mod streams; // AWS Lambda event definitions for DocumentDB #[cfg(feature = "documentdb")] +#[cfg_attr(docsrs, doc(cfg(feature = "documentdb")))] pub mod documentdb; /// AWS Lambda event definitions for EventBridge. #[cfg(feature = "eventbridge")] +#[cfg_attr(docsrs, doc(cfg(feature = "eventbridge")))] pub mod eventbridge; diff --git a/lambda-events/src/event/rabbitmq/mod.rs b/lambda-events/src/event/rabbitmq/mod.rs index 233272762..5b75e3c20 100644 --- a/lambda-events/src/event/rabbitmq/mod.rs +++ b/lambda-events/src/event/rabbitmq/mod.rs @@ -4,7 +4,8 @@ use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct RabbitMqEvent { #[serde(default)] @@ -15,18 +16,34 @@ pub struct RabbitMqEvent { #[serde(default)] #[serde(rename = "rmqMessagesByQueue")] pub messages_by_queue: HashMap>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct RabbitMqMessage { pub basic_properties: RabbitMqBasicProperties, #[serde(default)] pub data: Option, pub redelivered: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct RabbitMqBasicProperties where @@ -56,6 +73,13 @@ where pub app_id: Option, pub cluster_id: Option, pub body_size: u64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/s3/batch_job.rs b/lambda-events/src/event/s3/batch_job.rs index e3eb691e4..9b8f419b6 100644 --- a/lambda-events/src/event/s3/batch_job.rs +++ b/lambda-events/src/event/s3/batch_job.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `S3BatchJobEvent` encapsulates the detail of a s3 batch job +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3BatchJobEvent { @@ -10,17 +13,33 @@ pub struct S3BatchJobEvent { pub invocation_id: Option, pub job: S3BatchJob, pub tasks: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `S3BatchJob` whichs have the job id +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3BatchJob { #[serde(default)] pub id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `S3BatchJobTask` represents one task in the s3 batch job and have all task details +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3BatchJobTask { @@ -32,9 +51,17 @@ pub struct S3BatchJobTask { pub s3_version_id: Option, #[serde(default)] pub s3_bucket_arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `S3BatchJobResponse` is the response of a iven s3 batch job with the results +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3BatchJobResponse { @@ -45,9 +72,17 @@ pub struct S3BatchJobResponse { #[serde(default)] pub invocation_id: Option, pub results: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `S3BatchJobResult` represents the result of a given task +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3BatchJobResult { @@ -57,4 +92,11 @@ pub struct S3BatchJobResult { pub result_code: Option, #[serde(default)] pub result_string: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/event/s3/event.rs b/lambda-events/src/event/s3/event.rs index e062c7d23..68b9aee7b 100644 --- a/lambda-events/src/event/s3/event.rs +++ b/lambda-events/src/event/s3/event.rs @@ -1,18 +1,29 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; /// `S3Event` which wrap an array of `S3Event`Record +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3Event { #[serde(rename = "Records")] pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `S3EventRecord` which wrap record data +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3EventRecord { @@ -32,23 +43,47 @@ pub struct S3EventRecord { #[serde(default)] pub response_elements: HashMap, pub s3: S3Entity, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3UserIdentity { #[serde(default)] pub principal_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3RequestParameters { #[serde(default)] #[serde(rename = "sourceIPAddress")] pub source_ip_address: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3Entity { @@ -59,8 +94,16 @@ pub struct S3Entity { pub configuration_id: Option, pub bucket: S3Bucket, pub object: S3Object, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3Bucket { @@ -70,8 +113,16 @@ pub struct S3Bucket { pub owner_identity: Option, #[serde(default)] pub arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3Object { @@ -86,6 +137,13 @@ pub struct S3Object { pub e_tag: Option, #[serde(default)] pub sequencer: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/s3/object_lambda.rs b/lambda-events/src/event/s3/object_lambda.rs index 738cd72cc..be52e1f81 100644 --- a/lambda-events/src/event/s3/object_lambda.rs +++ b/lambda-events/src/event/s3/object_lambda.rs @@ -6,7 +6,8 @@ use std::collections::HashMap; use crate::custom_serde::{deserialize_headers, serialize_headers}; /// `S3ObjectLambdaEvent` contains data coming from S3 object lambdas -/// See: https://docs.aws.amazon.com/AmazonS3/latest/userguide/olap-writing-lambda.html +/// See: +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct S3ObjectLambdaEvent

@@ -24,43 +25,83 @@ where pub user_request: UserRequest, pub user_identity: UserIdentity, pub protocol_version: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `GetObjectContext` contains the input and output details /// for connections to Amazon S3 and S3 Object Lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct GetObjectContext { pub input_s3_url: String, pub output_route: String, pub output_token: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `HeadObjectContext` /// for connections to Amazon S3 and S3 Object Lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct HeadObjectContext { pub input_s3_url: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ListObjectsContext` /// for connections to Amazon S3 and S3 Object Lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ListObjectsContext { pub input_s3_url: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `ListObjectsV2Context` /// for connections to Amazon S3 and S3 Object Lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ListObjectsV2Context { pub input_s3_url: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `Configuration` contains information about the Object Lambda access point +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Configuration

@@ -72,9 +113,17 @@ where pub supporting_access_point_arn: String, #[serde(default, bound = "")] pub payload: P, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `UserRequest` contains information about the original call to S3 Object Lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UserRequest { @@ -82,9 +131,17 @@ pub struct UserRequest { #[serde(deserialize_with = "deserialize_headers", default)] #[serde(serialize_with = "serialize_headers")] pub headers: HeaderMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `UserIdentity` contains details about the identity that made the call to S3 Object Lambda +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UserIdentity { @@ -94,16 +151,32 @@ pub struct UserIdentity { pub account_id: String, pub access_key_id: String, pub session_context: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SessionContext { pub attributes: HashMap, #[serde(default)] pub session_issuer: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SessionIssuer { @@ -112,6 +185,13 @@ pub struct SessionIssuer { pub arn: String, pub account_id: String, pub user_name: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/secretsmanager/mod.rs b/lambda-events/src/event/secretsmanager/mod.rs index 3ed8d2389..9502d654e 100644 --- a/lambda-events/src/event/secretsmanager/mod.rs +++ b/lambda-events/src/event/secretsmanager/mod.rs @@ -1,11 +1,21 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "PascalCase")] pub struct SecretsManagerSecretRotationEvent { pub step: String, pub secret_id: String, pub client_request_token: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/ses/mod.rs b/lambda-events/src/event/ses/mod.rs index 2a60957ab..31a55ea3f 100644 --- a/lambda-events/src/event/ses/mod.rs +++ b/lambda-events/src/event/ses/mod.rs @@ -1,15 +1,26 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `SimpleEmailEvent` is the outer structure of an event sent via SES. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailEvent { #[serde(rename = "Records")] pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailRecord { #[serde(default)] @@ -17,17 +28,33 @@ pub struct SimpleEmailRecord { #[serde(default)] pub event_source: Option, pub ses: SimpleEmailService, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailService { pub mail: SimpleEmailMessage, pub receipt: SimpleEmailReceipt, pub content: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailMessage { pub common_headers: SimpleEmailCommonHeaders, @@ -39,9 +66,17 @@ pub struct SimpleEmailMessage { pub headers_truncated: bool, #[serde(default)] pub message_id: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailReceipt { pub recipients: Vec, @@ -55,18 +90,34 @@ pub struct SimpleEmailReceipt { pub virus_verdict: SimpleEmailVerdict, pub action: SimpleEmailReceiptAction, pub processing_time_millis: i64, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailHeader { #[serde(default)] pub name: Option, #[serde(default)] pub value: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailCommonHeaders { pub from: Vec, @@ -79,13 +130,21 @@ pub struct SimpleEmailCommonHeaders { pub date: Option, #[serde(default)] pub subject: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `SimpleEmailReceiptAction` is a logical union of fields present in all action /// Types. For example, the FunctionARN and InvocationType fields are only /// present for the Lambda Type, and the BucketName and ObjectKey fields are only /// present for the S3 Type. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailReceiptAction { #[serde(default)] @@ -100,22 +159,45 @@ pub struct SimpleEmailReceiptAction { pub invocation_type: Option, pub function_arn: Option, pub organization_arn: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailVerdict { #[serde(default)] pub status: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } pub type SimpleEmailDispositionValue = String; /// `SimpleEmailDisposition` disposition return for SES to control rule functions -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SimpleEmailDisposition { pub disposition: SimpleEmailDispositionValue, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/sns/mod.rs b/lambda-events/src/event/sns/mod.rs index 0fda569d1..d40dd45ca 100644 --- a/lambda-events/src/event/sns/mod.rs +++ b/lambda-events/src/event/sns/mod.rs @@ -1,5 +1,7 @@ use chrono::{DateTime, Utc}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::{deserialize_lambda_map, deserialize_nullish_boolean}; @@ -7,14 +9,24 @@ use crate::custom_serde::{deserialize_lambda_map, deserialize_nullish_boolean}; /// The `Event` notification event handled by Lambda /// /// [https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html](https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct SnsEvent { pub records: Vec, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// SnsRecord stores information about each record of a SNS event -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct SnsRecord { /// A string containing the event source. @@ -28,10 +40,19 @@ pub struct SnsRecord { /// An SNS object representing the SNS message. pub sns: SnsMessage, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// SnsMessage stores information about each record of a SNS event -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct SnsMessage { /// The type of SNS message. For a lambda event, this should always be **Notification** @@ -76,20 +97,38 @@ pub struct SnsMessage { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub message_attributes: HashMap, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// An alternate `Event` notification event to use alongside `SnsRecordObj` and `SnsMessageObj` if you want to deserialize an object inside your SNS messages rather than getting an `Option` message /// /// [https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html](https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html) +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] #[serde(bound(deserialize = "T: DeserializeOwned"))] pub struct SnsEventObj { pub records: Vec>, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// Alternative to `SnsRecord`, used alongside `SnsEventObj` and `SnsMessageObj` when deserializing nested objects from within SNS messages) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[non_exhaustive] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] #[serde(bound(deserialize = "T: DeserializeOwned"))] pub struct SnsRecordObj { @@ -104,11 +143,20 @@ pub struct SnsRecordObj { /// An SNS object representing the SNS message. pub sns: SnsMessageObj, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// Alternate version of `SnsMessage` to use in conjunction with `SnsEventObj` and `SnsRecordObj` for deserializing the message into a struct of type `T` +#[non_exhaustive] #[serde_with::serde_as] -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] #[serde(bound(deserialize = "T: DeserializeOwned"))] pub struct SnsMessageObj { @@ -156,6 +204,14 @@ pub struct SnsMessageObj { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub message_attributes: HashMap, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// Structured metadata items (such as timestamps, geospatial data, signatures, and identifiers) about the message. @@ -163,6 +219,7 @@ pub struct SnsMessageObj { /// Message attributes are optional and separate from—but are sent together with—the message body. The receiver can use this information to decide how to handle the message without having to process the message body first. /// /// Additional details can be found in the [SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html) +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct MessageAttribute { /// The data type of the attribute. Per the [SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html), lambda notifications, this will only be **String** or **Binary**. @@ -172,8 +229,17 @@ pub struct MessageAttribute { /// The user-specified message attribute value. #[serde(rename = "Value")] pub value: String, + + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudWatchAlarmPayload { @@ -188,8 +254,16 @@ pub struct CloudWatchAlarmPayload { pub alarm_arn: String, pub old_state_value: String, pub trigger: CloudWatchAlarmTrigger, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudWatchAlarmTrigger { @@ -208,8 +282,16 @@ pub struct CloudWatchAlarmTrigger { pub unit: Option, #[serde(default)] pub dimensions: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudWatchMetricDataQuery { @@ -220,8 +302,16 @@ pub struct CloudWatchMetricDataQuery { pub period: Option, #[serde(default, deserialize_with = "deserialize_nullish_boolean")] pub return_data: bool, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudWatchMetricStat { @@ -229,8 +319,16 @@ pub struct CloudWatchMetricStat { pub period: i64, pub stat: String, pub unit: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CloudWatchMetric { @@ -238,12 +336,27 @@ pub struct CloudWatchMetric { pub dimensions: Vec, pub metric_name: Option, pub namespace: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct CloudWatchDimension { pub name: String, pub value: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] @@ -312,7 +425,7 @@ mod test { } let parsed: SnsEventObj = serde_json::from_slice(data).unwrap(); - println!("{:?}", parsed); + println!("{parsed:?}"); assert_eq!(parsed.records[0].sns.message.foo, "Hello world!"); assert_eq!(parsed.records[0].sns.message.bar, 123); diff --git a/lambda-events/src/event/sqs/mod.rs b/lambda-events/src/event/sqs/mod.rs index 9dd69f66a..0a380734d 100644 --- a/lambda-events/src/event/sqs/mod.rs +++ b/lambda-events/src/event/sqs/mod.rs @@ -1,16 +1,27 @@ use crate::{custom_serde::deserialize_lambda_map, encodings::Base64Data}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; use std::collections::HashMap; /// The Event sent to Lambda from SQS. Contains 1 or more individual SQS Messages +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsEvent { #[serde(rename = "Records")] pub records: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// An individual SQS Message, its metadata, and Message Attributes +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsMessage { @@ -38,9 +49,17 @@ pub struct SqsMessage { pub event_source: Option, #[serde(default)] pub aws_region: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// Alternative to `SqsEvent` to be used alongside `SqsMessageObj` when you need to deserialize a nested object into a struct of type `T` within the SQS Message rather than just using the raw SQS Message string +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] #[serde(bound(deserialize = "T: DeserializeOwned"))] @@ -48,9 +67,17 @@ pub struct SqsEventObj { #[serde(rename = "Records")] #[serde(bound(deserialize = "T: DeserializeOwned"))] pub records: Vec>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// Alternative to `SqsMessage` to be used alongside `SqsEventObj` when you need to deserialize a nested object into a struct of type `T` within the SQS Message rather than just using the raw SQS Message string +#[non_exhaustive] #[serde_with::serde_as] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(bound(deserialize = "T: DeserializeOwned"))] @@ -83,8 +110,16 @@ pub struct SqsMessageObj { pub event_source: Option, #[serde(default)] pub aws_region: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsMessageAttribute { @@ -96,39 +131,79 @@ pub struct SqsMessageAttribute { pub binary_list_values: Vec, #[serde(default)] pub data_type: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsBatchResponse { pub batch_item_failures: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct BatchItemFailure { pub item_identifier: String, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// The Event sent to Lambda from the SQS API. Contains 1 or more individual SQS Messages +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] #[serde(bound(deserialize = "T: DeserializeOwned"))] pub struct SqsApiEventObj { #[serde(bound(deserialize = "T: DeserializeOwned"))] pub messages: Vec>, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// The Event sent to Lambda from SQS API. Contains 1 or more individual SQS Messages +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsApiEvent { pub messages: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } -/// Alternative to SqsApiEvent to be used alongside SqsApiMessageObj when you need to +/// Alternative to SqsApiEvent to be used alongside `SqsApiMessageObj` when you need to /// deserialize a nested object into a struct of type T within the SQS Message rather /// than just using the raw SQS Message string +#[non_exhaustive] #[serde_with::serde_as] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(bound(deserialize = "T: DeserializeOwned"))] @@ -153,9 +228,17 @@ pub struct SqsApiMessageObj { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub message_attributes: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// An individual SQS API Message, its metadata, and Message Attributes +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "PascalCase")] pub struct SqsApiMessage { @@ -176,6 +259,13 @@ pub struct SqsApiMessage { #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] pub message_attributes: HashMap, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } #[cfg(test)] diff --git a/lambda-events/src/event/streams/mod.rs b/lambda-events/src/event/streams/mod.rs index 9e0fd76fe..9f2391c8b 100644 --- a/lambda-events/src/event/streams/mod.rs +++ b/lambda-events/src/event/streams/mod.rs @@ -1,46 +1,96 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "catch-all-fields")] +use serde_json::Value; /// `KinesisEventResponse` is the outer structure to report batch item failures for KinesisEvent. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisEventResponse { pub batch_item_failures: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `KinesisBatchItemFailure` is the individual record which failed processing. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct KinesisBatchItemFailure { #[serde(default)] pub item_identifier: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `DynamoDbEventResponse` is the outer structure to report batch item failures for DynamoDBEvent. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct DynamoDbEventResponse { pub batch_item_failures: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `DynamoDbBatchItemFailure` is the individual record which failed processing. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct DynamoDbBatchItemFailure { #[serde(default)] pub item_identifier: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `SqsEventResponse` is the outer structure to report batch item failures for SQSEvent. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsEventResponse { pub batch_item_failures: Vec, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } /// `SqsBatchItemFailure` is the individual record which failed processing. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[non_exhaustive] +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SqsBatchItemFailure { #[serde(default)] pub item_identifier: Option, + /// Catchall to catch any additional fields that were present but not explicitly defined by this struct. + /// Enabled with Cargo feature `catch-all-fields`. + /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored. + #[cfg(feature = "catch-all-fields")] + #[cfg_attr(docsrs, doc(cfg(feature = "catch-all-fields")))] + #[serde(flatten)] + pub other: serde_json::Map, } diff --git a/lambda-events/src/fixtures/example-apigw-request-catch-all.json b/lambda-events/src/fixtures/example-apigw-request-catch-all.json new file mode 100644 index 000000000..fe1955f46 --- /dev/null +++ b/lambda-events/src/fixtures/example-apigw-request-catch-all.json @@ -0,0 +1,137 @@ +{ + "resource": "/{proxy+}", + "path": "/hello/world", + "httpMethod": "POST", + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "cache-control": "no-cache", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Content-Type": "application/json", + "headerName": "headerValue", + "Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", + "Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f", + "User-Agent": "PostmanRuntime/2.4.5", + "Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==", + "X-Forwarded-For": "54.240.196.186, 54.182.214.83", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "cache-control": [ + "no-cache" + ], + "CloudFront-Forwarded-Proto": [ + "https" + ], + "CloudFront-Is-Desktop-Viewer": [ + "true" + ], + "CloudFront-Is-Mobile-Viewer": [ + "false" + ], + "CloudFront-Is-SmartTV-Viewer": [ + "false" + ], + "CloudFront-Is-Tablet-Viewer": [ + "false" + ], + "CloudFront-Viewer-Country": [ + "US" + ], + "Content-Type": [ + "application/json" + ], + "headerName": [ + "headerValue" + ], + "Host": [ + "gy415nuibc.execute-api.us-east-1.amazonaws.com" + ], + "Postman-Token": [ + "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f" + ], + "User-Agent": [ + "PostmanRuntime/2.4.5" + ], + "Via": [ + "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)" + ], + "X-Amz-Cf-Id": [ + "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==" + ], + "X-Forwarded-For": [ + "54.240.196.186, 54.182.214.83" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": { + "name": "me" + }, + "multiValueQueryStringParameters": { + "name": [ + "me" + ] + }, + "pathParameters": { + "proxy": "hello/world" + }, + "stageVariables": { + "stageVariableName": "stageVariableValue" + }, + "requestContext": { + "accountId": "12345678912", + "resourceId": "roq9wj", + "path": "/hello/world", + "stage": "testStage", + "domainName": "gy415nuibc.execute-api.us-east-2.amazonaws.com", + "domainPrefix": "y0ne18dixk", + "requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33", + "protocol": "HTTP/1.1", + "identity": { + "cognitoIdentityPoolId": "theCognitoIdentityPoolId", + "accountId": "theAccountId", + "cognitoIdentityId": "theCognitoIdentityId", + "caller": "theCaller", + "apiKey": "theApiKey", + "apiKeyId": "theApiKeyId", + "accessKey": "ANEXAMPLEOFACCESSKEY", + "sourceIp": "192.168.196.186", + "cognitoAuthenticationType": "theCognitoAuthenticationType", + "cognitoAuthenticationProvider": "theCognitoAuthenticationProvider", + "userArn": "theUserArn", + "userAgent": "PostmanRuntime/2.4.5", + "user": "theUser", + "otherField": 2345 + }, + "authorizer": { + "principalId": "admin", + "clientId": 1, + "clientName": "Exata" + }, + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "requestTime": "15/May/2020:06:01:09 +0000", + "requestTimeEpoch": 1589522469693, + "apiId": "gy415nuibc" + }, + "body": "{\r\n\t\"a\": 1\r\n}", + "otherField": "foobar" +} diff --git a/lambda-events/src/fixtures/example-appsync-direct-resolver.json b/lambda-events/src/fixtures/example-appsync-direct-resolver.json new file mode 100644 index 000000000..9a8048765 --- /dev/null +++ b/lambda-events/src/fixtures/example-appsync-direct-resolver.json @@ -0,0 +1,64 @@ +{ + "arguments": { + "input": "foo" + }, + "identity": { + "sourceIp": [ + "x.x.x.x" + ], + "userArn": "arn:aws:iam::123456789012:user/appsync", + "accountId": "666666666666", + "user": "AIDAAAAAAAAAAAAAAAAAA" + }, + "info": { + "fieldName": "greet", + "parentTypeName": "Query", + "selectionSetGraphQL": "", + "selectionSetList": [], + "variables": { + "inputVar": "foo" + } + }, + "prev": null, + "request": { + "domainName": null, + "headers": { + "accept": "application/json, text/plain, */*", + "accept-encoding": "gzip, deflate, br, zstd", + "accept-language": "en-US,en;q=0.9,ja;q=0.8,en-GB;q=0.7", + "cloudfront-forwarded-proto": "https", + "cloudfront-is-desktop-viewer": "true", + "cloudfront-is-mobile-viewer": "false", + "cloudfront-is-smarttv-viewer": "false", + "cloudfront-is-tablet-viewer": "false", + "cloudfront-viewer-asn": "17676", + "cloudfront-viewer-country": "JP", + "content-length": "40", + "content-type": "application/json", + "host": "2ojpkjk2ejb57l7stgad5o4qiq.appsync-api.ap-northeast-1.amazonaws.com", + "origin": "https://ap-northeast-1.console.aws.amazon.com", + "priority": "u=1, i", + "referer": "https://ap-northeast-1.console.aws.amazon.com/", + "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Microsoft Edge\";v=\"138\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0", + "via": "2.0 ee337d4db5c7ebfdc8ec0798a1ede776.cloudfront.net (CloudFront)", + "x-amz-cf-id": "O3ZflUCq6_TzxjouyYB3zg7-kl7Ze-gXbniM2jJ3hAOfDFpPMGRu3Q==", + "x-amz-user-agent": "AWS-Console-AppSync/", + "x-amzn-appsync-is-vpce-request": "false", + "x-amzn-remote-ip": "x.x.x.x", + "x-amzn-requestid": "7ada8740-bbf4-49e8-bf45-f10b3d67159b", + "x-amzn-trace-id": "Root=1-68713e21-7a03739120ad60703e794b22", + "x-api-key": "***", + "x-forwarded-for": "***", + "x-forwarded-port": "443", + "x-forwarded-proto": "https" + } + }, + "source": null, + "stash": {} +} \ No newline at end of file diff --git a/lambda-events/src/lib.rs b/lambda-events/src/lib.rs index e21cdc13a..d35dbd760 100644 --- a/lambda-events/src/lib.rs +++ b/lambda-events/src/lib.rs @@ -1,13 +1,17 @@ #![deny(rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(feature = "http")] +#[cfg_attr(docsrs, doc(cfg(feature = "http")))] pub use http; #[cfg(feature = "query_map")] +#[cfg_attr(docsrs, doc(cfg(feature = "query_map")))] pub use query_map; mod custom_serde; /// Encodings used in AWS Lambda json event values. pub mod encodings; #[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub mod time_window; /// AWS Lambda event definitions. @@ -15,168 +19,210 @@ pub mod event; /// AWS Lambda event definitions for activemq. #[cfg(feature = "activemq")] +#[cfg_attr(docsrs, doc(cfg(feature = "activemq")))] pub use event::activemq; /// AWS Lambda event definitions for alb. #[cfg(feature = "alb")] +#[cfg_attr(docsrs, doc(cfg(feature = "alb")))] pub use event::alb; /// AWS Lambda event definitions for apigw. #[cfg(feature = "apigw")] +#[cfg_attr(docsrs, doc(cfg(feature = "apigw")))] pub use event::apigw; /// AWS Lambda event definitions for appsync. #[cfg(feature = "appsync")] +#[cfg_attr(docsrs, doc(cfg(feature = "appsync")))] pub use event::appsync; /// AWS Lambda event definitions for autoscaling. #[cfg(feature = "autoscaling")] +#[cfg_attr(docsrs, doc(cfg(feature = "autoscaling")))] pub use event::autoscaling; /// AWS Lambda event definitions for chime_bot. #[cfg(feature = "chime_bot")] +#[cfg_attr(docsrs, doc(cfg(feature = "chime_bot")))] pub use event::chime_bot; /// AWS Lambda event definitions for clientvpn. #[cfg(feature = "clientvpn")] +#[cfg_attr(docsrs, doc(cfg(feature = "clientvpn")))] pub use event::clientvpn; /// AWS Lambda event definitions for cloudformation #[cfg(feature = "cloudformation")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudformation")))] pub use event::cloudformation; /// AWS Lambda event definitions for CloudWatch alarms. #[cfg(feature = "cloudwatch_alarms")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudwatch_alarms")))] pub use event::cloudwatch_alarms; /// AWS Lambda event definitions for CloudWatch events. #[cfg(feature = "cloudwatch_events")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudwatch_events")))] pub use event::cloudwatch_events; /// AWS Lambda event definitions for cloudwatch_logs. #[cfg(feature = "cloudwatch_logs")] +#[cfg_attr(docsrs, doc(cfg(feature = "cloudwatch_logs")))] pub use event::cloudwatch_logs; /// AWS Lambda event definitions for code_commit. #[cfg(feature = "code_commit")] +#[cfg_attr(docsrs, doc(cfg(feature = "code_commit")))] pub use event::code_commit; /// AWS Lambda event definitions for codebuild. #[cfg(feature = "codebuild")] +#[cfg_attr(docsrs, doc(cfg(feature = "codebuild")))] pub use event::codebuild; /// AWS Lambda event definitions for codedeploy. #[cfg(feature = "codedeploy")] +#[cfg_attr(docsrs, doc(cfg(feature = "codedeploy")))] pub use event::codedeploy; /// AWS Lambda event definitions for codepipeline_cloudwatch. #[cfg(feature = "codepipeline_cloudwatch")] +#[cfg_attr(docsrs, doc(cfg(feature = "codepipeline_cloudwatch")))] pub use event::codepipeline_cloudwatch; /// AWS Lambda event definitions for codepipeline_job. #[cfg(feature = "codepipeline_job")] +#[cfg_attr(docsrs, doc(cfg(feature = "codepipeline_job")))] pub use event::codepipeline_job; /// AWS Lambda event definitions for cognito. #[cfg(feature = "cognito")] +#[cfg_attr(docsrs, doc(cfg(feature = "cognito")))] pub use event::cognito; /// AWS Lambda event definitions for config. #[cfg(feature = "config")] +#[cfg_attr(docsrs, doc(cfg(feature = "config")))] pub use event::config; /// AWS Lambda event definitions for connect. #[cfg(feature = "connect")] +#[cfg_attr(docsrs, doc(cfg(feature = "connect")))] pub use event::connect; /// AWS Lambda event definitions for dynamodb. #[cfg(feature = "dynamodb")] +#[cfg_attr(docsrs, doc(cfg(feature = "dynamodb")))] pub use event::dynamodb; /// AWS Lambda event definitions for ecr_scan. #[cfg(feature = "ecr_scan")] +#[cfg_attr(docsrs, doc(cfg(feature = "ecr_scan")))] pub use event::ecr_scan; /// AWS Lambda event definitions for firehose. #[cfg(feature = "firehose")] +#[cfg_attr(docsrs, doc(cfg(feature = "firehose")))] pub use event::firehose; /// AWS Lambda event definitions for iam. #[cfg(feature = "iam")] +#[cfg_attr(docsrs, doc(cfg(feature = "iam")))] pub use event::iam; /// AWS Lambda event definitions for iot. #[cfg(feature = "iot")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot")))] pub use event::iot; /// AWS Lambda event definitions for iot_1_click. #[cfg(feature = "iot_1_click")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot_1_click")))] pub use event::iot_1_click; /// AWS Lambda event definitions for iot_button. #[cfg(feature = "iot_button")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot_button")))] pub use event::iot_button; /// AWS Lambda event definitions for iot_deprecated. #[cfg(feature = "iot_deprecated")] +#[cfg_attr(docsrs, doc(cfg(feature = "iot_deprecated")))] pub use event::iot_deprecated; /// AWS Lambda event definitions for kafka. #[cfg(feature = "kafka")] +#[cfg_attr(docsrs, doc(cfg(feature = "kafka")))] pub use event::kafka; /// AWS Lambda event definitions for kinesis. #[cfg(feature = "kinesis")] +#[cfg_attr(docsrs, doc(cfg(feature = "kinesis")))] pub use event::kinesis; /// AWS Lambda event definitions for kinesis_analytics. #[cfg(feature = "kinesis_analytics")] +#[cfg_attr(docsrs, doc(cfg(feature = "kinesis_analytics")))] pub use event::kinesis::analytics as kinesis_analytics; /// AWS Lambda event definitions for lambda_function_urls. #[cfg(feature = "lambda_function_urls")] +#[cfg_attr(docsrs, doc(cfg(feature = "lambda_function_urls")))] pub use event::lambda_function_urls; /// AWS Lambda event definitions for lex. #[cfg(feature = "lex")] +#[cfg_attr(docsrs, doc(cfg(feature = "lex")))] pub use event::lex; /// AWS Lambda event definitions for rabbitmq. #[cfg(feature = "rabbitmq")] +#[cfg_attr(docsrs, doc(cfg(feature = "rabbitmq")))] pub use event::rabbitmq; /// AWS Lambda event definitions for s3. #[cfg(feature = "s3")] +#[cfg_attr(docsrs, doc(cfg(feature = "s3")))] pub use event::s3; /// AWS Lambda event definitions for s3_batch_job. #[cfg(feature = "s3")] +#[cfg_attr(docsrs, doc(cfg(feature = "s3")))] pub use event::s3::batch_job as s3_batch_job; /// AWS Lambda event definitions for secretsmanager. #[cfg(feature = "secretsmanager")] +#[cfg_attr(docsrs, doc(cfg(feature = "secretsmanager")))] pub use event::secretsmanager; /// AWS Lambda event definitions for ses. #[cfg(feature = "ses")] +#[cfg_attr(docsrs, doc(cfg(feature = "ses")))] pub use event::ses; /// AWS Lambda event definitions for SNS. #[cfg(feature = "sns")] +#[cfg_attr(docsrs, doc(cfg(feature = "sns")))] pub use event::sns; /// AWS Lambda event definitions for SQS. #[cfg(feature = "sqs")] +#[cfg_attr(docsrs, doc(cfg(feature = "sqs")))] pub use event::sqs; /// AWS Lambda event definitions for streams. #[cfg(feature = "streams")] +#[cfg_attr(docsrs, doc(cfg(feature = "streams")))] pub use event::streams; /// AWS Lambda event definitions for documentdb. #[cfg(feature = "documentdb")] +#[cfg_attr(docsrs, doc(cfg(feature = "documentdb")))] pub use event::documentdb; /// AWS Lambda event definitions for EventBridge. #[cfg(feature = "eventbridge")] +#[cfg_attr(docsrs, doc(cfg(feature = "eventbridge")))] pub use event::eventbridge; diff --git a/lambda-events/src/time_window.rs b/lambda-events/src/time_window.rs index edc9beb5f..d67a8e91f 100644 --- a/lambda-events/src/time_window.rs +++ b/lambda-events/src/time_window.rs @@ -5,8 +5,9 @@ use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; /// `Window` is the object that captures the time window for the records in the event when using the tumbling windows feature -/// Kinesis: https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-windows -/// DDB: https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-windows +/// Kinesis: +/// DDB: +#[non_exhaustive] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Window { @@ -24,8 +25,9 @@ impl Default for Window { } /// `TimeWindowProperties` is the object that captures properties that relate to the tumbling windows feature -/// Kinesis: https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-windows -/// DDB: https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-windows +/// Kinesis: +/// DDB: +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TimeWindowProperties { @@ -51,8 +53,9 @@ pub struct TimeWindowProperties { } /// `TimeWindowEventResponseProperties` is the object that captures response properties that relate to the tumbling windows feature -/// Kinesis: https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html#services-kinesis-windows -/// DDB: https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-windows +/// Kinesis: +/// DDB: +#[non_exhaustive] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TimeWindowEventResponseProperties { diff --git a/lambda-extension/Cargo.toml b/lambda-extension/Cargo.toml index 16b6dace9..427b744a1 100644 --- a/lambda-extension/Cargo.toml +++ b/lambda-extension/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "lambda-extension" -version = "0.11.0" +version = "0.12.2" edition = "2021" +rust-version = "1.82.0" authors = [ "David Calavera ", "Harold Sun ", @@ -19,13 +20,12 @@ tracing = ["lambda_runtime_api_client/tracing"] [dependencies] async-stream = "0.3" -bytes = { workspace = true } chrono = { workspace = true, features = ["serde"] } http = { workspace = true } http-body-util = { workspace = true } hyper = { workspace = true, features = ["http1", "client", "server"] } hyper-util = { workspace = true } -lambda_runtime_api_client = { version = "0.11", path = "../lambda-runtime-api-client" } +lambda_runtime_api_client = { version = "0.12.3", path = "../lambda-runtime-api-client" } serde = { version = "1", features = ["derive"] } serde_json = "^1" tokio = { version = "1.0", features = [ @@ -37,3 +37,6 @@ tokio = { version = "1.0", features = [ tokio-stream = "0.1.2" tower = { workspace = true, features = ["make", "util"] } tracing = { version = "0.1", features = ["log"] } + +[package.metadata.docs.rs] +all-features = true diff --git a/lambda-extension/src/extension.rs b/lambda-extension/src/extension.rs index 15e0befd3..e7d83847e 100644 --- a/lambda-extension/src/extension.rs +++ b/lambda-extension/src/extension.rs @@ -259,7 +259,7 @@ where let io = TokioIo::new(tcp); tokio::task::spawn(async move { if let Err(err) = http1::Builder::new().serve_connection(io, make_service).await { - println!("Error serving connection: {:?}", err); + println!("Error serving connection: {err:?}"); } }); } @@ -305,7 +305,7 @@ where let io = TokioIo::new(tcp); tokio::task::spawn(async move { if let Err(err) = http1::Builder::new().serve_connection(io, make_service).await { - println!("Error serving connection: {:?}", err); + println!("Error serving connection: {err:?}"); } }); } diff --git a/lambda-extension/src/lib.rs b/lambda-extension/src/lib.rs index 81c16337f..b6aec18fd 100644 --- a/lambda-extension/src/lib.rs +++ b/lambda-extension/src/lib.rs @@ -1,6 +1,7 @@ #![deny(clippy::all, clippy::cargo)] #![allow(clippy::multiple_crate_versions, clippy::type_complexity)] #![warn(missing_docs, nonstandard_style, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg))] //! This module includes utilities to create Lambda Runtime Extensions. //! @@ -25,6 +26,7 @@ pub mod requests; /// Utilities to initialize and use `tracing` and `tracing-subscriber` in Lambda Functions. #[cfg(feature = "tracing")] +#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))] pub use lambda_runtime_api_client::tracing; /// Execute the given events processor diff --git a/lambda-extension/src/logs.rs b/lambda-extension/src/logs.rs index c3b0cda28..541dedc2d 100644 --- a/lambda-extension/src/logs.rs +++ b/lambda-extension/src/logs.rs @@ -12,7 +12,7 @@ use tracing::{error, trace}; use crate::{Error, ExtensionError}; /// Payload received from the Lambda Logs API -/// See: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-logs-api.html#runtimes-logs-api-msg +/// See: #[derive(Clone, Debug, Deserialize, PartialEq)] pub struct LambdaLog { /// Time when the log was generated diff --git a/lambda-http/Cargo.toml b/lambda-http/Cargo.toml index 46198dca5..f5f3dfe18 100644 --- a/lambda-http/Cargo.toml +++ b/lambda-http/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "lambda_http" -version = "0.14.0" +version = "0.17.0" authors = [ "David Calavera ", "Harold Sun ", ] edition = "2021" +rust-version = "1.82.0" description = "Application Load Balancer and API Gateway event types for AWS Lambda" keywords = ["AWS", "Lambda", "APIGateway", "ALB", "API"] license = "Apache-2.0" @@ -22,6 +23,7 @@ apigw_http = [] apigw_websockets = [] alb = [] pass_through = [] +catch-all-fields = ["aws_lambda_events/catch-all-fields"] tracing = ["lambda_runtime/tracing"] # enables access to the Tracing utilities opentelemetry = ["lambda_runtime/opentelemetry"] # enables access to the OpenTelemetry layers and utilities anyhow = ["lambda_runtime/anyhow"] # enables From for Diagnostic for anyhow error types, see README.md for more info @@ -29,16 +31,14 @@ eyre = ["lambda_runtime/eyre"] # enables From for Diagnostic for eyre error t miette = ["lambda_runtime/miette"] # enables From for Diagnostic for miette error types, see README.md for more info [dependencies] -base64 = { workspace = true } bytes = { workspace = true } encoding_rs = "0.8" -futures = { workspace = true } futures-util = { workspace = true } http = { workspace = true } http-body = { workspace = true } http-body-util = { workspace = true } hyper = { workspace = true } -lambda_runtime = { version = "0.13.0", path = "../lambda-runtime" } +lambda_runtime = { version = "0.14.3", path = "../lambda-runtime" } mime = "0.3" percent-encoding = "2.2" pin-project-lite = { workspace = true } @@ -50,14 +50,17 @@ url = "2.2" [dependencies.aws_lambda_events] path = "../lambda-events" -version = "0.16.0" +version = "0.18.0" default-features = false features = ["alb", "apigw"] [dev-dependencies] -axum-core = "0.4.3" -axum-extra = { version = "0.9.2", features = ["query"] } -lambda_runtime_api_client = { version = "0.11.1", path = "../lambda-runtime-api-client" } +axum-core = "0.5.4" +axum-extra = { version = "0.10.2", features = ["query"] } +lambda_runtime_api_client = { version = "0.12.3", path = "../lambda-runtime-api-client" } log = "^0.4" maplit = "1.0" tokio = { version = "1.0", features = ["macros"] } + +[package.metadata.docs.rs] +all-features = true diff --git a/lambda-http/src/deserializer.rs b/lambda-http/src/deserializer.rs index 4c0ad519c..e0da5e0e1 100644 --- a/lambda-http/src/deserializer.rs +++ b/lambda-http/src/deserializer.rs @@ -20,7 +20,7 @@ impl<'de> Deserialize<'de> for LambdaRequest { where D: serde::Deserializer<'de>, { - let raw_value: Box = Box::deserialize(deserializer)?; + let raw_value: Box = Box::::deserialize(deserializer)?; let data = raw_value.get(); #[cfg(feature = "apigw_rest")] @@ -61,7 +61,7 @@ mod tests { LambdaRequest::ApiGatewayV1(req) => { assert_eq!("12345678912", req.request_context.account_id.unwrap()); } - other => panic!("unexpected request variant: {:?}", other), + other => panic!("unexpected request variant: {other:?}"), } } @@ -74,7 +74,7 @@ mod tests { LambdaRequest::ApiGatewayV2(req) => { assert_eq!("123456789012", req.request_context.account_id.unwrap()); } - other => panic!("unexpected request variant: {:?}", other), + other => panic!("unexpected request variant: {other:?}"), } } @@ -87,7 +87,7 @@ mod tests { LambdaRequest::ApiGatewayV1(req) => { assert_eq!("123456789012", req.request_context.account_id.unwrap()); } - other => panic!("unexpected request variant: {:?}", other), + other => panic!("unexpected request variant: {other:?}"), } } @@ -100,7 +100,7 @@ mod tests { LambdaRequest::ApiGatewayV2(req) => { assert_eq!("123456789012", req.request_context.account_id.unwrap()); } - other => panic!("unexpected request variant: {:?}", other), + other => panic!("unexpected request variant: {other:?}"), } } @@ -118,7 +118,7 @@ mod tests { req.request_context.elb.target_group_arn.unwrap() ); } - other => panic!("unexpected request variant: {:?}", other), + other => panic!("unexpected request variant: {other:?}"), } } @@ -132,7 +132,7 @@ mod tests { LambdaRequest::WebSocket(req) => { assert_eq!("CONNECT", req.request_context.event_type.unwrap()); } - other => panic!("unexpected request variant: {:?}", other), + other => panic!("unexpected request variant: {other:?}"), } } diff --git a/lambda-http/src/ext/extensions.rs b/lambda-http/src/ext/extensions.rs index cfbdaec29..65bf8ac07 100644 --- a/lambda-http/src/ext/extensions.rs +++ b/lambda-http/src/ext/extensions.rs @@ -7,12 +7,14 @@ use lambda_runtime::Context; use crate::request::RequestContext; /// ALB/API gateway pre-parsed http query string parameters +#[non_exhaustive] #[derive(Clone)] pub(crate) struct QueryStringParameters(pub(crate) QueryMap); /// API gateway pre-extracted url path parameters /// /// These will always be empty for ALB requests +#[non_exhaustive] #[derive(Clone)] pub(crate) struct PathParameters(pub(crate) QueryMap); @@ -20,10 +22,12 @@ pub(crate) struct PathParameters(pub(crate) QueryMap); /// [stage variables](https://docs.aws.amazon.com/apigateway/latest/developerguide/stage-variables.html) /// /// These will always be empty for ALB requests +#[non_exhaustive] #[derive(Clone)] pub(crate) struct StageVariables(pub(crate) QueryMap); /// ALB/API gateway raw http path without any stage information +#[non_exhaustive] #[derive(Clone)] pub(crate) struct RawHttpPath(pub(crate) String); diff --git a/lambda-http/src/ext/request.rs b/lambda-http/src/ext/request.rs index c56518f66..38e45afa1 100644 --- a/lambda-http/src/ext/request.rs +++ b/lambda-http/src/ext/request.rs @@ -12,6 +12,7 @@ use crate::Body; /// Request payload deserialization errors /// /// Returned by [`RequestPayloadExt::payload()`] +#[non_exhaustive] #[derive(Debug)] pub enum PayloadError { /// Returned when `application/json` bodies fail to deserialize a payload @@ -21,6 +22,7 @@ pub enum PayloadError { } /// Indicates a problem processing a JSON payload. +#[non_exhaustive] #[derive(Debug)] pub enum JsonPayloadError { /// Problem deserializing a JSON payload. @@ -28,6 +30,7 @@ pub enum JsonPayloadError { } /// Indicates a problem processing an x-www-form-urlencoded payload. +#[non_exhaustive] #[derive(Debug)] pub enum FormUrlEncodedPayloadError { /// Problem deserializing an x-www-form-urlencoded payload. diff --git a/lambda-http/src/lib.rs b/lambda-http/src/lib.rs index 92cd5dae1..60e279c7f 100644 --- a/lambda-http/src/lib.rs +++ b/lambda-http/src/lib.rs @@ -1,4 +1,5 @@ #![warn(missing_docs, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg))] //#![deny(warnings)] //! Enriches the `lambda` crate with [`http`](https://github.com/hyperium/http) //! types targeting AWS [ALB](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html), [API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) REST and HTTP API lambda integrations. @@ -67,6 +68,7 @@ extern crate maplit; pub use http::{self, Response}; /// Utilities to initialize and use `tracing` and `tracing-subscriber` in Lambda Functions. #[cfg(feature = "tracing")] +#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))] pub use lambda_runtime::tracing; use lambda_runtime::Diagnostic; pub use lambda_runtime::{self, service_fn, tower, Context, Error, LambdaEvent, Service}; @@ -100,7 +102,7 @@ use std::{ }; mod streaming; -pub use streaming::run_with_streaming_response; +pub use streaming::{run_with_streaming_response, StreamAdapter}; /// Type alias for `http::Request`s with a fixed [`Body`](enum.Body.html) type pub type Request = http::Request; @@ -108,6 +110,7 @@ pub type Request = http::Request; /// Future that will convert an [`IntoResponse`] into an actual [`LambdaResponse`] /// /// This is used by the `Adapter` wrapper and is completely internal to the `lambda_http::run` function. +#[non_exhaustive] #[doc(hidden)] pub enum TransformResponse<'a, R, E> { Request(RequestOrigin, RequestFuture<'a, R, E>), @@ -141,6 +144,7 @@ where /// Wraps a `Service` in a `Service>` /// /// This is completely internal to the `lambda_http::run` function. +#[non_exhaustive] #[doc(hidden)] pub struct Adapter<'a, R, S> { service: S, @@ -188,7 +192,7 @@ where /// Runtime APIs](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html). /// /// This takes care of transforming the LambdaEvent into a [`Request`] and then -/// converting the result into a [`LambdaResponse`]. +/// converting the result into a `LambdaResponse`. pub async fn run<'a, R, S, E>(handler: S) -> Result<(), Error> where S: Service, diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index a9281b461..fc88fc4a6 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -39,6 +39,7 @@ use url::Url; /// /// This is not intended to be a type consumed by crate users directly. The order /// of the variants are notable. Serde will try to deserialize in this order. +#[non_exhaustive] #[doc(hidden)] #[derive(Debug)] pub enum LambdaRequest { @@ -85,6 +86,7 @@ impl LambdaRequest { pub type RequestFuture<'a, R, E> = Pin> + Send + 'a>>; /// Represents the origin from which the lambda was requested from. +#[non_exhaustive] #[doc(hidden)] #[derive(Debug, Clone)] pub enum RequestOrigin { @@ -388,6 +390,7 @@ fn apigw_path_with_stage(stage: &Option, path: &str) -> String { /// Event request context as an enumeration of request contexts /// for both ALB and API Gateway and HTTP API events +#[non_exhaustive] #[derive(Deserialize, Debug, Clone, Serialize)] #[serde(untagged)] pub enum RequestContext { diff --git a/lambda-http/src/response.rs b/lambda-http/src/response.rs index e8528fdf2..6fad374b0 100644 --- a/lambda-http/src/response.rs +++ b/lambda-http/src/response.rs @@ -40,6 +40,7 @@ const TEXT_ENCODING_PREFIXES: [&str; 5] = [ const TEXT_ENCODING_SUFFIXES: [&str; 3] = ["+xml", "+yaml", "+json"]; /// Representation of Lambda response +#[non_exhaustive] #[doc(hidden)] #[derive(Serialize, Debug)] #[serde(untagged)] @@ -62,6 +63,7 @@ impl LambdaResponse { Body::Empty => (false, None), b @ Body::Text(_) => (false, Some(b)), b @ Body::Binary(_) => (true, Some(b)), + _ => (false, None), }; let headers = parts.headers; @@ -69,14 +71,22 @@ impl LambdaResponse { match request_origin { #[cfg(feature = "apigw_rest")] - RequestOrigin::ApiGatewayV1 => LambdaResponse::ApiGatewayV1(ApiGatewayProxyResponse { - body, - is_base64_encoded, - status_code: status_code as i64, + RequestOrigin::ApiGatewayV1 => LambdaResponse::ApiGatewayV1({ + let mut response = ApiGatewayProxyResponse::default(); + + response.body = body; + response.is_base64_encoded = is_base64_encoded; + response.status_code = status_code as i64; // Explicitly empty, as API gateway v1 will merge "headers" and // "multi_value_headers" fields together resulting in duplicate response headers. - headers: HeaderMap::new(), - multi_value_headers: headers, + response.headers = HeaderMap::new(); + response.multi_value_headers = headers; + // Today, this implementation doesn't provide any additional fields + #[cfg(feature = "catch-all-fields")] + { + response.other = Default::default(); + } + response }), #[cfg(feature = "apigw_http")] RequestOrigin::ApiGatewayV2 => { @@ -92,42 +102,64 @@ impl LambdaResponse { .collect(); headers.remove(SET_COOKIE); - LambdaResponse::ApiGatewayV2(ApiGatewayV2httpResponse { - body, - is_base64_encoded, - status_code: status_code as i64, - cookies, + LambdaResponse::ApiGatewayV2({ + let mut response = ApiGatewayV2httpResponse::default(); + response.body = body; + response.is_base64_encoded = is_base64_encoded; + response.status_code = status_code as i64; + response.cookies = cookies; // API gateway v2 doesn't have "multi_value_headers" field. Duplicate headers // are combined with commas and included in the headers field. - headers, - multi_value_headers: HeaderMap::new(), + response.headers = headers; + response.multi_value_headers = HeaderMap::new(); + // Today, this implementation doesn't provide any additional fields + #[cfg(feature = "catch-all-fields")] + { + response.other = Default::default(); + } + response }) } #[cfg(feature = "alb")] - RequestOrigin::Alb => LambdaResponse::Alb(AlbTargetGroupResponse { - body, - status_code: status_code as i64, - is_base64_encoded, + RequestOrigin::Alb => LambdaResponse::Alb({ + let mut response = AlbTargetGroupResponse::default(); + + response.body = body; + response.is_base64_encoded = is_base64_encoded; + response.status_code = status_code as i64; // ALB responses are used for ALB integration, which can be configured to use // either "headers" or "multi_value_headers" field. We need to return both fields // to ensure both configuration work correctly. - headers: headers.clone(), - multi_value_headers: headers, - status_description: Some(format!( + response.headers = headers.clone(); + response.multi_value_headers = headers; + response.status_description = Some(format!( "{} {}", status_code, parts.status.canonical_reason().unwrap_or_default() - )), + )); + // Today, this implementation doesn't provide any additional fields + #[cfg(feature = "catch-all-fields")] + { + response.other = Default::default(); + } + response }), #[cfg(feature = "apigw_websockets")] - RequestOrigin::WebSocket => LambdaResponse::ApiGatewayV1(ApiGatewayProxyResponse { - body, - is_base64_encoded, - status_code: status_code as i64, + RequestOrigin::WebSocket => LambdaResponse::ApiGatewayV1({ + let mut response = ApiGatewayProxyResponse::default(); + response.body = body; + response.is_base64_encoded = is_base64_encoded; + response.status_code = status_code as i64; // Explicitly empty, as API gateway v1 will merge "headers" and // "multi_value_headers" fields together resulting in duplicate response headers. - headers: HeaderMap::new(), - multi_value_headers: headers, + response.headers = HeaderMap::new(); + response.multi_value_headers = headers; + // Today, this implementation doesn't provide any additional fields + #[cfg(feature = "catch-all-fields")] + { + response.other = Default::default(); + } + response }), #[cfg(feature = "pass_through")] RequestOrigin::PassThrough => { @@ -153,7 +185,7 @@ impl LambdaResponse { /// /// Types that implement this trait can be used as return types for handler functions. pub trait IntoResponse { - /// Transform into a Response Future + /// Transform into a `Response` Future fn into_response(self) -> ResponseFuture; } diff --git a/lambda-http/src/streaming.rs b/lambda-http/src/streaming.rs index a93408b41..ed61c7736 100644 --- a/lambda-http/src/streaming.rs +++ b/lambda-http/src/streaming.rs @@ -1,22 +1,89 @@ -use crate::{http::header::SET_COOKIE, request::LambdaRequest, tower::ServiceBuilder, Request, RequestExt}; +use crate::{http::header::SET_COOKIE, request::LambdaRequest, Request, RequestExt}; use bytes::Bytes; -pub use http::{self, Response}; -use http_body::Body; -use lambda_runtime::Diagnostic; -pub use lambda_runtime::{self, tower::ServiceExt, Error, LambdaEvent, MetadataPrelude, Service, StreamResponse}; -use std::{ +use core::{ fmt::Debug, pin::Pin, task::{Context, Poll}, }; -use tokio_stream::Stream; +use futures_util::{Stream, TryFutureExt}; +pub use http::{self, Response}; +use http_body::Body; +use lambda_runtime::{ + tower::{ + util::{MapRequest, MapResponse}, + ServiceBuilder, ServiceExt, + }, + Diagnostic, +}; +pub use lambda_runtime::{Error, LambdaEvent, MetadataPrelude, Service, StreamResponse}; +use std::{future::Future, marker::PhantomData}; + +/// An adapter that lifts a standard [`Service`] into a +/// [`Service>`] which produces streaming Lambda HTTP +/// responses. +#[non_exhaustive] +pub struct StreamAdapter<'a, S, B> { + service: S, + _phantom_data: PhantomData<&'a B>, +} + +impl<'a, S, B, E> From for StreamAdapter<'a, S, B> +where + S: Service, Error = E>, + S::Future: Send + 'a, + B: Body + Unpin + Send + 'static, + B::Data: Into + Send, + B::Error: Into + Send + Debug, +{ + fn from(service: S) -> Self { + StreamAdapter { + service, + _phantom_data: PhantomData, + } + } +} + +impl<'a, S, B, E> Service> for StreamAdapter<'a, S, B> +where + S: Service, Error = E>, + S::Future: Send + 'a, + B: Body + Unpin + Send + 'static, + B::Data: Into + Send, + B::Error: Into + Send + Debug, +{ + type Response = StreamResponse>; + type Error = E; + type Future = Pin> + Send + 'a>>; -/// Starts the Lambda Rust runtime and stream response back [Configure Lambda -/// Streaming Response](https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html). + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, req: LambdaEvent) -> Self::Future { + let event: Request = req.payload.into(); + Box::pin( + self.service + .call(event.with_lambda_context(req.context)) + .map_ok(into_stream_response), + ) + } +} + +/// Builds a streaming-aware Tower service from a `Service` **without** +/// boxing its future (no heap allocation / vtable). /// -/// This takes care of transforming the LambdaEvent into a [`Request`] and -/// accepts [`http::Response`] as response. -pub async fn run_with_streaming_response<'a, S, B, E>(handler: S) -> Result<(), Error> +/// Transforms `LambdaEvent` into `Request` with Lambda context +/// and wraps `Response` into `StreamResponse>`. +/// +/// Used internally by [`run_with_streaming_response`]; not part of the public +/// API. +#[allow(clippy::type_complexity)] +fn into_stream_service<'a, S, B, E>( + handler: S, +) -> MapResponse< + MapRequest) -> Request>, + impl FnOnce(Response) -> StreamResponse> + Clone, +> where S: Service, Error = E>, S::Future: Send + 'a, @@ -25,41 +92,63 @@ where B::Data: Into + Send, B::Error: Into + Send + Debug, { - let svc = ServiceBuilder::new() + ServiceBuilder::new() .map_request(|req: LambdaEvent| { let event: Request = req.payload.into(); event.with_lambda_context(req.context) }) .service(handler) - .map_response(|res| { - let (parts, body) = res.into_parts(); - - let mut prelude_headers = parts.headers; - - let cookies = prelude_headers.get_all(SET_COOKIE); - let cookies = cookies - .iter() - .map(|c| String::from_utf8_lossy(c.as_bytes()).to_string()) - .collect::>(); + .map_response(into_stream_response) +} - prelude_headers.remove(SET_COOKIE); +/// Converts an `http::Response` into a streaming Lambda response. +fn into_stream_response(res: Response) -> StreamResponse> +where + B: Body + Unpin + Send + 'static, + B::Data: Into + Send, + B::Error: Into + Send + Debug, +{ + let (parts, body) = res.into_parts(); - let metadata_prelude = MetadataPrelude { - headers: prelude_headers, - status_code: parts.status, - cookies, - }; + let mut headers = parts.headers; + let cookies = headers + .get_all(SET_COOKIE) + .iter() + .map(|c| String::from_utf8_lossy(c.as_bytes()).to_string()) + .collect::>(); + headers.remove(SET_COOKIE); - StreamResponse { - metadata_prelude, - stream: BodyStream { body }, - } - }); + StreamResponse { + metadata_prelude: MetadataPrelude { + headers, + status_code: parts.status, + cookies, + }, + stream: BodyStream { body }, + } +} - lambda_runtime::run(svc).await +/// Runs the Lambda runtime with a handler that returns **streaming** HTTP +/// responses. +/// +/// See the [AWS docs for response streaming]. +/// +/// [AWS docs for response streaming]: +/// https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html +pub async fn run_with_streaming_response<'a, S, B, E>(handler: S) -> Result<(), Error> +where + S: Service, Error = E>, + S::Future: Send + 'a, + E: Debug + Into, + B: Body + Unpin + Send + 'static, + B::Data: Into + Send, + B::Error: Into + Send + Debug, +{ + lambda_runtime::run(into_stream_service(handler)).await } pin_project_lite::pin_project! { +#[non_exhaustive] pub struct BodyStream { #[pin] pub(crate) body: B, @@ -85,3 +174,47 @@ where } } } + +#[cfg(test)] +mod test_stream_adapter { + use super::*; + + use crate::Body; + use http::StatusCode; + + // A middleware that logs requests before forwarding them to another service + struct LogService { + inner: S, + } + + impl Service> for LogService + where + S: Service>, + { + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, event: LambdaEvent) -> Self::Future { + println!("Lambda event: {event:#?}"); + self.inner.call(event) + } + } + + #[test] + fn stream_adapter_is_boxable() { + // Works with a concrete service stack (no boxing) + let svc = ServiceBuilder::new() + .layer_fn(|service| LogService { inner: service }) + .layer_fn(StreamAdapter::from) + .service_fn( + |_req: Request| async move { http::Response::builder().status(StatusCode::OK).body(Body::Empty) }, + ); + // Also works when the stack is boxed (type-erased) + let _boxed_svc = svc.boxed(); + } +} diff --git a/lambda-integration-tests/Cargo.toml b/lambda-integration-tests/Cargo.toml index ee44a9694..2e9817c2b 100644 --- a/lambda-integration-tests/Cargo.toml +++ b/lambda-integration-tests/Cargo.toml @@ -1,8 +1,9 @@ [package] -name = "aws_lambda_rust_integration_tests" +name = "lambda-integration-tests" version = "0.1.0" authors = ["Maxime David"] edition = "2021" +rust-version = "1.82.0" description = "AWS Lambda Runtime integration tests" license = "Apache-2.0" repository = "https://github.com/awslabs/aws-lambda-rust-runtime" @@ -11,7 +12,7 @@ keywords = ["AWS", "Lambda", "API"] readme = "../README.md" [dependencies] -lambda_runtime = { path = "../lambda-runtime" } +lambda_runtime = { path = "../lambda-runtime", features = ["tracing", "graceful-shutdown"] } aws_lambda_events = { path = "../lambda-events" } serde_json = "1.0.121" tokio = { version = "1", features = ["full"] } @@ -19,7 +20,9 @@ serde = { version = "1.0.204", features = ["derive"] } [dev-dependencies] reqwest = { version = "0.12.5", features = ["blocking"] } -openssl = { version = "0.10", features = ["vendored"] } + +[features] +catch-all-fields = ["aws_lambda_events/catch-all-fields"] [[bin]] name = "helloworld" diff --git a/lambda-integration-tests/src/authorizer.rs b/lambda-integration-tests/src/authorizer.rs index 41ddd2d8e..23f17b296 100644 --- a/lambda-integration-tests/src/authorizer.rs +++ b/lambda-integration-tests/src/authorizer.rs @@ -34,20 +34,36 @@ async fn func( } fn allow(method_arn: &str) -> ApiGatewayCustomAuthorizerResponse { - let stmt = IamPolicyStatement { - action: vec!["execute-api:Invoke".to_string()], - resource: vec![method_arn.to_owned()], - effect: aws_lambda_events::iam::IamPolicyEffect::Allow, - condition: None, + let stmt = { + let mut statement = IamPolicyStatement::default(); + statement.action = vec!["execute-api:Invoke".to_string()]; + statement.resource = vec![method_arn.to_owned()]; + statement.effect = aws_lambda_events::iam::IamPolicyEffect::Allow; + statement.condition = None; + #[cfg(feature = "catch-all-fields")] + { + statement.other = Default::default(); + } + statement }; - let policy = ApiGatewayCustomAuthorizerPolicy { - version: Some("2012-10-17".to_string()), - statement: vec![stmt], + let policy = { + let mut policy = ApiGatewayCustomAuthorizerPolicy::default(); + policy.version = Some("2012-10-17".to_string()); + policy.statement = vec![stmt]; + #[cfg(feature = "catch-all-fields")] + { + policy.other = Default::default(); + } + policy }; - ApiGatewayCustomAuthorizerResponse { - principal_id: Some("user".to_owned()), - policy_document: policy, - context: json!({ "hello": "world" }), - usage_identifier_key: None, + let mut response = ApiGatewayCustomAuthorizerResponse::default(); + response.principal_id = Some("user".to_owned()); + response.policy_document = policy; + response.context = json!({ "hello": "world" }); + response.usage_identifier_key = None; + #[cfg(feature = "catch-all-fields")] + { + response.other = Default::default(); } + response } diff --git a/lambda-integration-tests/src/helloworld.rs b/lambda-integration-tests/src/helloworld.rs index 9989da438..2eafc409a 100644 --- a/lambda-integration-tests/src/helloworld.rs +++ b/lambda-integration-tests/src/helloworld.rs @@ -8,6 +8,7 @@ use lambda_runtime::{service_fn, tracing, Error, LambdaEvent}; async fn main() -> Result<(), Error> { tracing::init_default_subscriber(); let func = service_fn(func); + lambda_runtime::spawn_graceful_shutdown_handler(|| async move {}).await; lambda_runtime::run(func).await?; Ok(()) } @@ -15,12 +16,18 @@ async fn main() -> Result<(), Error> { async fn func(_event: LambdaEvent) -> Result { let mut headers = HeaderMap::new(); headers.insert("content-type", "text/html".parse().unwrap()); - let resp = ApiGatewayProxyResponse { - status_code: 200, - multi_value_headers: headers.clone(), - is_base64_encoded: false, - body: Some("Hello world!".into()), - headers, + let resp = { + let mut response = ApiGatewayProxyResponse::default(); + response.status_code = 200; + response.multi_value_headers = headers.clone(); + response.is_base64_encoded = false; + response.body = Some("Hello world!".into()); + response.headers = headers; + #[cfg(feature = "catch-all-fields")] + { + response.other = Default::default(); + } + response }; Ok(resp) } diff --git a/lambda-runtime-api-client/Cargo.toml b/lambda-runtime-api-client/Cargo.toml index 57fc4bca5..39311a8c1 100644 --- a/lambda-runtime-api-client/Cargo.toml +++ b/lambda-runtime-api-client/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "lambda_runtime_api_client" -version = "0.11.1" +version = "0.12.4" edition = "2021" +rust-version = "1.82.0" authors = [ "David Calavera ", "Harold Sun ", @@ -32,7 +33,8 @@ hyper-util = { workspace = true, features = [ "tokio", ] } tower = { workspace = true, features = ["util"] } -tower-service = { workspace = true } -tokio = { version = "1.0", features = ["io-util"] } tracing = { version = "0.1", features = ["log"], optional = true } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "json", "env-filter"], optional = true } + +[package.metadata.docs.rs] +all-features = true diff --git a/lambda-runtime-api-client/src/body/channel.rs b/lambda-runtime-api-client/src/body/channel.rs index 275746556..f1e094c3c 100644 --- a/lambda-runtime-api-client/src/body/channel.rs +++ b/lambda-runtime-api-client/src/body/channel.rs @@ -1,5 +1,5 @@ //! Body::channel utilities. Extracted from Hyper under MIT license. -//! https://github.com/hyperium/hyper/blob/master/LICENSE +//! use std::{ pin::Pin, @@ -31,7 +31,7 @@ impl DecodedLength { } } - /// Converts to an Option representing a Known or Unknown length. + /// Converts to an `Option` representing a Known or Unknown length. pub(crate) fn into_opt(self) -> Option { match self { DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None, diff --git a/lambda-runtime-api-client/src/body/mod.rs b/lambda-runtime-api-client/src/body/mod.rs index 467356829..13bfcaa03 100644 --- a/lambda-runtime-api-client/src/body/mod.rs +++ b/lambda-runtime-api-client/src/body/mod.rs @@ -1,5 +1,5 @@ //! HTTP body utilities. Extracted from Axum under MIT license. -//! https://github.com/tokio-rs/axum/blob/main/axum/LICENSE +//! use crate::{BoxError, Error}; use bytes::Bytes; diff --git a/lambda-runtime-api-client/src/body/sender.rs b/lambda-runtime-api-client/src/body/sender.rs index 0e008454c..14c1d918e 100644 --- a/lambda-runtime-api-client/src/body/sender.rs +++ b/lambda-runtime-api-client/src/body/sender.rs @@ -1,5 +1,5 @@ //! Body::channel utilities. Extracted from Hyper under MIT license. -//! https://github.com/hyperium/hyper/blob/master/LICENSE +//! use crate::Error; use std::task::{Context, Poll}; diff --git a/lambda-runtime-api-client/src/body/watch.rs b/lambda-runtime-api-client/src/body/watch.rs index a5f8ae412..f31f4f273 100644 --- a/lambda-runtime-api-client/src/body/watch.rs +++ b/lambda-runtime-api-client/src/body/watch.rs @@ -1,5 +1,5 @@ //! Body::channel utilities. Extracted from Hyper under MIT license. -//! https://github.com/hyperium/hyper/blob/master/LICENSE +//! //! An SPSC broadcast channel. //! diff --git a/lambda-runtime-api-client/src/error.rs b/lambda-runtime-api-client/src/error.rs index dbb87b642..d8ff30b22 100644 --- a/lambda-runtime-api-client/src/error.rs +++ b/lambda-runtime-api-client/src/error.rs @@ -1,5 +1,5 @@ //! Extracted from Axum under MIT license. -//! https://github.com/tokio-rs/axum/blob/main/axum/LICENSE +//! use std::{error::Error as StdError, fmt}; pub use tower::BoxError; /// Errors that can happen when using axum. diff --git a/lambda-runtime-api-client/src/lib.rs b/lambda-runtime-api-client/src/lib.rs index 00c00e4c9..3df616ab4 100644 --- a/lambda-runtime-api-client/src/lib.rs +++ b/lambda-runtime-api-client/src/lib.rs @@ -1,6 +1,7 @@ #![deny(clippy::all, clippy::cargo)] #![warn(missing_docs, nonstandard_style, rust_2018_idioms)] #![allow(clippy::multiple_crate_versions)] +#![cfg_attr(docsrs, feature(doc_cfg))] //! This crate includes a base HTTP client to interact with //! the AWS Lambda Runtime API. @@ -22,6 +23,7 @@ pub use error::*; pub mod body; #[cfg(feature = "tracing")] +#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))] pub mod tracing; /// API client to interact with the AWS Lambda Runtime API. diff --git a/lambda-runtime-api-client/src/tracing.rs b/lambda-runtime-api-client/src/tracing.rs index 09f41991f..5aa9bfa17 100644 --- a/lambda-runtime-api-client/src/tracing.rs +++ b/lambda-runtime-api-client/src/tracing.rs @@ -14,12 +14,16 @@ pub use tracing::*; /// Re-export the `tracing-subscriber` crate to build your own subscribers. pub use tracing_subscriber as subscriber; +use tracing_subscriber::fmt::MakeWriter; const DEFAULT_LOG_LEVEL: &str = "INFO"; /// Initialize `tracing-subscriber` with default logging options. /// -/// This function uses environment variables set with [Lambda's advance logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/) +/// The default subscriber writes logs to STDOUT in the current context. +/// If you want to customize the writer, see [`init_default_subscriber_with_writer()`]. +/// +/// This function uses environment variables set with [Lambda's advanced logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/) /// if they're configured for your function. /// /// This subscriber sets the logging level based on environment variables: @@ -31,6 +35,32 @@ const DEFAULT_LOG_LEVEL: &str = "INFO"; /// If the `AWS_LAMBDA_LOG_FORMAT` environment variable is set to `JSON`, the log lines will be formatted as json objects, /// otherwise they will be formatted with the default tracing format. pub fn init_default_subscriber() { + init_default_subscriber_with_writer(std::io::stdout); +} + +/// Initialize `tracing-subscriber` with default logging options, and a custom writer. +/// +/// You might want to avoid writing to STDOUT in the local context via [`init_default_subscriber()`], if you have a high-throughput Lambdas that involve +/// a lot of async concurrency. Since, writing to STDOUT can briefly block your tokio runtime - ref [tracing #2653](https://github.com/tokio-rs/tracing/issues/2653). +/// In that case, you might prefer to use [tracing_appender::NonBlocking](https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.NonBlocking.html) instead - particularly if your Lambda is fairly long-running and stays warm. +/// Though, note that you are then responsible +/// for ensuring gracefuls shutdown. See [aws-samples/graceful-shutdown-with-aws-lambda](https://github.com/aws-samples/graceful-shutdown-with-aws-lambda) for a complete example. +/// +/// This function uses environment variables set with [Lambda's advanced logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/) +/// if they're configured for your function. +/// +/// This subscriber sets the logging level based on environment variables: +/// - if `AWS_LAMBDA_LOG_LEVEL` is set, it takes precedence over any other environment variables. +/// - if `AWS_LAMBDA_LOG_LEVEL` is not set, check if `RUST_LOG` is set. +/// - if none of those two variables are set, use `INFO` as the logging level. +/// +/// The logging format can also be changed based on Lambda's advanced logging controls. +/// If the `AWS_LAMBDA_LOG_FORMAT` environment variable is set to `JSON`, the log lines will be formatted as json objects, +/// otherwise they will be formatted with the default tracing format. +pub fn init_default_subscriber_with_writer(writer: Writer) +where + Writer: for<'writer> MakeWriter<'writer> + Send + Sync + 'static, +{ let log_format = env::var("AWS_LAMBDA_LOG_FORMAT").unwrap_or_default(); let log_level_str = env::var("AWS_LAMBDA_LOG_LEVEL").or_else(|_| env::var("RUST_LOG")); let log_level = @@ -43,7 +73,8 @@ pub fn init_default_subscriber() { EnvFilter::builder() .with_default_directive(log_level.into()) .from_env_lossy(), - ); + ) + .with_writer(writer); if log_format.eq_ignore_ascii_case("json") { collector.json().init() diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index b4a7ad3d4..f8192e5cd 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "lambda_runtime" -version = "0.13.0" +version = "0.14.4" authors = [ "David Calavera ", "Harold Sun ", ] description = "AWS Lambda Runtime" edition = "2021" +rust-version = "1.82.0" license = "Apache-2.0" repository = "https://github.com/awslabs/aws-lambda-rust-runtime" categories = ["web-programming::http-server"] @@ -20,6 +21,10 @@ opentelemetry = ["opentelemetry-semantic-conventions"] # enables access to the O anyhow = ["dep:anyhow"] # enables From for Diagnostic for anyhow error types, see README.md for more info eyre = ["dep:eyre"] # enables From for Diagnostic for eyre error types, see README.md for more info miette = ["dep:miette"] # enables From for Diagnostic for miette error types, see README.md for more info +# TODO: remove tokio/rt and rt-multi-thread from non-feature-flagged dependencies in new breaking version, since they are unused: +# as well as default features +# https://github.com/awslabs/aws-lambda-rust-runtime/issues/984 +graceful-shutdown = ["tokio/rt", "tokio/signal", "dep:lambda-extension"] [dependencies] anyhow = { version = "1.0.86", optional = true } @@ -29,24 +34,18 @@ bytes = { workspace = true } eyre = { version = "0.6.12", optional = true } futures = { workspace = true } http = { workspace = true } -http-body = { workspace = true } http-body-util = { workspace = true } http-serde = { workspace = true } hyper = { workspace = true, features = ["http1", "client"] } -hyper-util = { workspace = true, features = [ - "client", - "client-legacy", - "http1", - "tokio", -] } -lambda_runtime_api_client = { version = "0.11.1", path = "../lambda-runtime-api-client", default-features = false } +lambda-extension = { version = "0.12.2", path = "../lambda-extension", default-features = false, optional = true } +lambda_runtime_api_client = { version = "0.12.3", path = "../lambda-runtime-api-client", default-features = false } miette = { version = "7.2.0", optional = true } -opentelemetry-semantic-conventions = { version = "0.27", optional = true, features = ["semconv_experimental"] } +opentelemetry-semantic-conventions = { version = "0.31", optional = true, features = ["semconv_experimental"] } pin-project = "1" serde = { version = "1", features = ["derive", "rc"] } serde_json = "^1" serde_path_to_error = "0.1.11" -tokio = { version = "1.0", features = [ +tokio = { version = "1.46", features = [ "macros", "io-util", "sync", @@ -54,11 +53,10 @@ tokio = { version = "1.0", features = [ ] } tokio-stream = "0.1.2" tower = { workspace = true, features = ["util"] } -tower-layer = { workspace = true } tracing = { version = "0.1", features = ["log"] } [dev-dependencies] -httpmock = "0.7.0" +httpmock = "0.8.1" hyper-util = { workspace = true, features = [ "client", "client-legacy", @@ -67,4 +65,13 @@ hyper-util = { workspace = true, features = [ "server-auto", "tokio", ] } +# pin back to pre-1.2.1 to avoid breaking rust MSRV of 1.81: +# https://github.com/hsivonen/idna_adapter/commit/f948802e3a2ae936eec51886eefbd7d536a28791 +idna_adapter = "=1.2.0" +# Self dependency to enable the graceful-shutdown feature for tests +lambda_runtime = { path = ".", features = ["tracing", "graceful-shutdown"] } pin-project-lite = { workspace = true } +tracing-appender = "0.2" + +[package.metadata.docs.rs] +all-features = true diff --git a/lambda-runtime/src/diagnostic.rs b/lambda-runtime/src/diagnostic.rs index c03ce284b..60917e317 100644 --- a/lambda-runtime/src/diagnostic.rs +++ b/lambda-runtime/src/diagnostic.rs @@ -119,6 +119,7 @@ impl From for Diagnostic { } #[cfg(feature = "anyhow")] +#[cfg_attr(docsrs, doc(cfg(feature = "anyhow")))] impl From for Diagnostic { fn from(value: anyhow::Error) -> Diagnostic { Diagnostic { @@ -129,6 +130,7 @@ impl From for Diagnostic { } #[cfg(feature = "eyre")] +#[cfg_attr(docsrs, doc(cfg(feature = "eyre")))] impl From for Diagnostic { fn from(value: eyre::Report) -> Diagnostic { Diagnostic { @@ -139,6 +141,7 @@ impl From for Diagnostic { } #[cfg(feature = "miette")] +#[cfg_attr(docsrs, doc(cfg(feature = "miette")))] impl From for Diagnostic { fn from(value: miette::Report) -> Diagnostic { Diagnostic { diff --git a/lambda-runtime/src/layers/api_response.rs b/lambda-runtime/src/layers/api_response.rs index e744cde10..453f8b4c4 100644 --- a/lambda-runtime/src/layers/api_response.rs +++ b/lambda-runtime/src/layers/api_response.rs @@ -85,7 +85,7 @@ where #[cfg(debug_assertions)] if req.parts.status.is_server_error() { error!("Lambda Runtime server returned an unexpected error"); - return RuntimeApiResponseFuture::Ready(Some(Err(req.parts.status.to_string().into()))); + return RuntimeApiResponseFuture::Ready(Box::new(Some(Err(req.parts.status.to_string().into())))); } // Utility closure to propagate potential error from conditionally executed trace @@ -98,22 +98,23 @@ where }; if let Err(err) = trace_fn() { error!(error = ?err, "Failed to parse raw JSON event received from Lambda. The handler will not be called. Log at TRACE level to see the payload."); - return RuntimeApiResponseFuture::Ready(Some(Err(err))); + return RuntimeApiResponseFuture::Ready(Box::new(Some(Err(err)))); }; let request_id = req.context.request_id.clone(); let lambda_event = match deserializer::deserialize::(&req.body, req.context) { Ok(lambda_event) => lambda_event, Err(err) => match build_event_error_request(&request_id, err) { - Ok(request) => return RuntimeApiResponseFuture::Ready(Some(Ok(request))), + Ok(request) => return RuntimeApiResponseFuture::Ready(Box::new(Some(Ok(request)))), Err(err) => { error!(error = ?err, "failed to build error response for Lambda Runtime API"); - return RuntimeApiResponseFuture::Ready(Some(Err(err))); + return RuntimeApiResponseFuture::Ready(Box::new(Some(Err(err)))); } }, }; - // Once the handler input has been generated successfully, the + // Once the handler input has been generated successfully, pass it through to inner services + // allowing processing both before reaching the handler function and after the handler completes. let fut = self.inner.call(lambda_event); RuntimeApiResponseFuture::Future(fut, request_id, PhantomData) } @@ -141,7 +142,10 @@ pub enum RuntimeApiResponseFuture, ), - Ready(Option, BoxError>>), + /// This variant is used in case the invocation fails to be processed into an event. + /// We box it to avoid bloating the size of the more likely variant, which is + /// the future that drives event processing. + Ready(Box, BoxError>>>), } impl Future diff --git a/lambda-runtime/src/layers/mod.rs b/lambda-runtime/src/layers/mod.rs index 1f07f1995..a05b6c674 100644 --- a/lambda-runtime/src/layers/mod.rs +++ b/lambda-runtime/src/layers/mod.rs @@ -14,4 +14,5 @@ pub use trace::TracingLayer; #[cfg(feature = "opentelemetry")] mod otel; #[cfg(feature = "opentelemetry")] +#[cfg_attr(docsrs, doc(cfg(feature = "opentelemetry")))] pub use otel::{OpenTelemetryFaasTrigger, OpenTelemetryLayer}; diff --git a/lambda-runtime/src/layers/otel.rs b/lambda-runtime/src/layers/otel.rs index f50f36f7b..5e96dfedf 100644 --- a/lambda-runtime/src/layers/otel.rs +++ b/lambda-runtime/src/layers/otel.rs @@ -1,7 +1,7 @@ use std::{fmt::Display, future::Future, pin::Pin, task}; use crate::LambdaInvocation; -use opentelemetry_semantic_conventions::trace as traceconv; +use opentelemetry_semantic_conventions::attribute; use pin_project::pin_project; use tower::{Layer, Service}; use tracing::{field, instrument::Instrumented, Instrument}; @@ -76,9 +76,9 @@ where "Lambda function invocation", "otel.name" = req.context.env_config.function_name, "otel.kind" = field::Empty, - { traceconv::FAAS_TRIGGER } = &self.otel_attribute_trigger, - { traceconv::FAAS_INVOCATION_ID } = req.context.request_id, - { traceconv::FAAS_COLDSTART } = self.coldstart + { attribute::FAAS_TRIGGER } = &self.otel_attribute_trigger, + { attribute::FAAS_INVOCATION_ID } = req.context.request_id, + { attribute::FAAS_COLDSTART } = self.coldstart ); // After the first execution, we can set 'coldstart' to false @@ -131,7 +131,7 @@ where } /// Represent the possible values for the OpenTelemetry `faas.trigger` attribute. -/// See https://opentelemetry.io/docs/specs/semconv/attributes-registry/faas/ for more details. +/// See for more details. #[derive(Default, Clone, Copy)] #[non_exhaustive] pub enum OpenTelemetryFaasTrigger { diff --git a/lambda-runtime/src/layers/panic.rs b/lambda-runtime/src/layers/panic.rs index 4b92e3c83..257a8f396 100644 --- a/lambda-runtime/src/layers/panic.rs +++ b/lambda-runtime/src/layers/panic.rs @@ -99,6 +99,8 @@ impl CatchPanicFuture<'_, F> { fn build_panic_diagnostic(err: &Box) -> Diagnostic { let error_message = if let Some(msg) = err.downcast_ref::<&str>() { format!("Lambda panicked: {msg}") + } else if let Some(msg) = err.downcast_ref::() { + format!("Lambda panicked: {msg}") } else { "Lambda panicked".to_string() }; diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 76d0562ae..cbcd0a9e1 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -1,6 +1,7 @@ #![deny(clippy::all, clippy::cargo)] #![allow(clippy::multiple_crate_versions)] #![warn(missing_docs, nonstandard_style, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg))] //! The mechanism available for defining a Lambda function is as follows: //! @@ -32,6 +33,7 @@ pub mod streaming; /// Utilities to initialize and use `tracing` and `tracing-subscriber` in Lambda Functions. #[cfg(feature = "tracing")] +#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))] pub use lambda_runtime_api_client::tracing; /// Types available to a Lambda function. @@ -123,3 +125,112 @@ where let runtime = Runtime::new(handler).layer(layers::TracingLayer::new()); runtime.run().await } + +/// Spawns a task that will be execute a provided async closure when the process +/// receives unix graceful shutdown signals. If the closure takes longer than 500ms +/// to execute, an unhandled `SIGKILL` signal might be received. +/// +/// You can use this future to execute cleanup or flush related logic prior to runtime shutdown. +/// +/// This function's returned future must be resolved prior to `lambda_runtime::run()`. +/// +/// Note that this implicitly also registers and drives a no-op internal extension that subscribes to no events. +/// This extension will be named `_lambda-rust-runtime-no-op-graceful-shutdown-helper`. This extension name +/// can not be reused by other registered extensions. This is necessary in order to receive graceful shutdown signals. +/// +/// This extension is cheap to run because it receives no events, but is not zero cost. If you have another extension +/// registered already, you might prefer to manually construct your own graceful shutdown handling without the dummy extension. +/// +/// For more information on general AWS Lambda graceful shutdown handling, see: +/// +/// +/// # Panics +/// +/// This function panics if: +/// - this function is called after `lambda_runtime::run()` +/// - this function is called outside of a context that has access to the tokio i/o +/// - the no-op extension cannot be registered +/// - either signal listener panics [tokio::signal::unix](https://docs.rs/tokio/latest/tokio/signal/unix/fn.signal.html#errors) +/// +/// # Example +/// ```no_run +/// use lambda_runtime::{Error, service_fn, LambdaEvent}; +/// use serde_json::Value; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Error> { +/// let func = service_fn(func); +/// +/// let (writer, log_guard) = tracing_appender::non_blocking(std::io::stdout()); +/// lambda_runtime::tracing::init_default_subscriber_with_writer(writer); +/// +/// let shutdown_hook = || async move { +/// std::mem::drop(log_guard); +/// }; +/// lambda_runtime::spawn_graceful_shutdown_handler(shutdown_hook).await; +/// +/// lambda_runtime::run(func).await?; +/// Ok(()) +/// } +/// +/// async fn func(event: LambdaEvent) -> Result { +/// Ok(event.payload) +/// } +/// ``` +#[cfg(all(unix, feature = "graceful-shutdown"))] +#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "graceful-shutdown"))))] +pub async fn spawn_graceful_shutdown_handler(shutdown_hook: impl FnOnce() -> Fut + Send + 'static) +where + Fut: Future + Send + 'static, +{ + // You need an extension registered with the Lambda orchestrator in order for your process + // to receive a SIGTERM for graceful shutdown. + // + // We accomplish this here by registering a no-op internal extension, which does not subscribe to any events. + // + // This extension is cheap to run since after it connects to the lambda orchestration, the connection + // will just wait forever for data to come, which never comes, so it won't cause wakes. + let extension = lambda_extension::Extension::new() + // Don't subscribe to any event types + .with_events(&[]) + // Internal extension names MUST be unique within a given Lambda function. + .with_extension_name("_lambda-rust-runtime-no-op-graceful-shutdown-helper") + // Extensions MUST be registered before calling lambda_runtime::run(), which ends the Init + // phase and begins the Invoke phase. + .register() + .await + .expect("could not register no-op extension for graceful shutdown"); + + tokio::task::spawn(async move { + let graceful_shutdown_future = async move { + let mut sigint = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt()).unwrap(); + let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).unwrap(); + tokio::select! { + _sigint = sigint.recv() => { + eprintln!("[runtime] SIGINT received"); + eprintln!("[runtime] Graceful shutdown in progress ..."); + shutdown_hook().await; + eprintln!("[runtime] Graceful shutdown completed"); + std::process::exit(0); + }, + _sigterm = sigterm.recv()=> { + eprintln!("[runtime] SIGTERM received"); + eprintln!("[runtime] Graceful shutdown in progress ..."); + shutdown_hook().await; + eprintln!("[runtime] Graceful shutdown completed"); + std::process::exit(0); + }, + } + }; + + let _: (_, ()) = tokio::join!( + // we always poll the graceful shutdown future first, + // which results in a smaller future due to lack of bookkeeping of which was last polled + biased; + graceful_shutdown_future, async { + // we suppress extension errors because we don't actually mind if it crashes, + // all we need to do is kick off the run so that lambda exits the init phase + let _ = extension.run().await; + }); + }); +} diff --git a/lambda-runtime/src/requests.rs b/lambda-runtime/src/requests.rs index e8b0183cf..ec1e6ae15 100644 --- a/lambda-runtime/src/requests.rs +++ b/lambda-runtime/src/requests.rs @@ -24,20 +24,6 @@ impl IntoRequest for NextEventRequest { } } -#[derive(Debug, Eq, PartialEq)] -pub struct NextEventResponse<'a> { - // lambda-runtime-aws-request-id - pub request_id: &'a str, - // lambda-runtime-deadline-ms - pub deadline: u64, - // lambda-runtime-invoked-function-arn - pub arn: &'a str, - // lambda-runtime-trace-id - pub trace_id: &'a str, - // the actual body, - pub body: Vec, -} - // /runtime/invocation/{AwsRequestId}/response pub(crate) struct EventCompletionRequest<'a, R, B, S, D, E> where diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs index 5749fbb79..517ee64fa 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime/src/runtime.rs @@ -369,7 +369,7 @@ mod endpoint_tests { }); let next_response = server.mock(|when, then| { when.method(POST) - .path(format!("/2018-06-01/runtime/invocation/{}/response", request_id)) + .path(format!("/2018-06-01/runtime/invocation/{request_id}/response")) .body("{}"); then.status(200).body(""); }); @@ -440,7 +440,7 @@ mod endpoint_tests { let next_response = server.mock(|when, then| { when.method(POST) - .path(format!("/2018-06-01/runtime/invocation/{}/error", request_id)) + .path(format!("/2018-06-01/runtime/invocation/{request_id}/error")) .header("lambda-runtime-function-error-type", "unhandled"); then.status(200).body(""); }); diff --git a/lambda-runtime/src/types.rs b/lambda-runtime/src/types.rs index ee09978f7..5e5f487a6 100644 --- a/lambda-runtime/src/types.rs +++ b/lambda-runtime/src/types.rs @@ -176,7 +176,7 @@ impl LambdaEvent { } /// Metadata prelude for a stream response. -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct MetadataPrelude { #[serde(with = "http_serde::status_code")] @@ -478,4 +478,22 @@ mod test { let _ = invoke_request_id(&headers); } + + #[test] + fn serde_metadata_prelude() { + let metadata_prelude = MetadataPrelude { + status_code: StatusCode::OK, + headers: { + let mut headers = HeaderMap::new(); + headers.insert("key", "val".parse().unwrap()); + headers + }, + cookies: vec!["cookie".to_string()], + }; + + let serialized = serde_json::to_string(&metadata_prelude).unwrap(); + let deserialized: MetadataPrelude = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(metadata_prelude, deserialized); + } } diff --git a/scripts/test-rie.sh b/scripts/test-rie.sh new file mode 100755 index 000000000..911cb3904 --- /dev/null +++ b/scripts/test-rie.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -euo pipefail + +EXAMPLE=${1:-basic-lambda} + +echo "Building Docker image with RIE for example: $EXAMPLE..." +docker build -f Dockerfile.rie --build-arg EXAMPLE=$EXAMPLE -t rust-lambda-rie-test . + +echo "Starting RIE container on port 9000..." +docker run -p 9000:8080 rust-lambda-rie-test & +CONTAINER_PID=$! + +echo "Container started. Test with:" +if [ "$EXAMPLE" = "basic-lambda" ]; then + echo "curl -XPOST 'http://localhost:9000/2015-03-31/functions/function/invocations' -d '{\"command\": \"test from RIE\"}' -H 'Content-Type: application/json'" +else + echo "For example '$EXAMPLE', check examples/$EXAMPLE/src/main.rs for the expected payload format." +fi +echo "" +echo "Press Ctrl+C to stop the container." + +wait $CONTAINER_PID \ No newline at end of file