diff --git a/.github/actions/rust-build/action.yml b/.github/actions/rust-build/action.yml index 85b5c0e8..6c393d1c 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 6894e331..624f96d6 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 cb23c289..f823dbb4 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 00000000..dd9bd68f --- /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 dfc59ee8..0327bd34 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 00000000..4e26c31c --- /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 f1e30d09..a4fd604b 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 00000000..5d777e2d --- /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 867e9c0d..7baf97c2 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 00000000..1a46b577 --- /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 ecfd7623..155b7ea1 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 1a93d85c..954b195c 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 52ebb843..51b708ec 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 61c9a537..8f30ffef 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 83cb9552..688a31a0 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 b7d5e515..87ec54fa 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 69ec04a0..e82dc1d3 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 76631bbd..2dd69db1 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 83aa48ab..2772f650 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 2a70dce3..6a2883f3 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 95050b9a..f02e4efb 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 42bb2253..4af67b8c 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 7d2e7ab4..93369e51 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 741ec713..24fbc8dc 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 f4048584..176bd54b 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 1039a139..a0267f97 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 3bc76936..85e97428 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 d7c7d725..f2b0b449 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 403309bf..2c01b833 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 d6d023d8..40f24d81 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 bd3b4e6c..87891ebc 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 daba3739..69b46ec2 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 4b9ef3da..fdbd79be 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 3eb5bfe9..0d647b05 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 0dd8629d..1a759371 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 454a970f..6e680aaf 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 d49c84e1..a021f4c2 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 2aad5886..514bef32 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 0df7d8e2..36efee36 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 8533c8e3..fe112b80 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 48e2ed51..d7cf1f13 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 2a745c7b..93aacca1 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 c2f813c3..dfef4c4b 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 b51eae8e..8d0e4575 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 daadd0eb..1a0747dd 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 553f7a3d..c6408676 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 ff1d10da..d20213e1 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 dccc1ec0..230ebc7e 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 1b1eea0a..421fe9ff 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 c6675e5a..84f7ac96 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 7871ce52..c9d8a2e4 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 1b8b1ba4..a0fb6b87 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 c757aa94..44c50167 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 513a6cd8..8adb9024 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 69366957..d21df0b9 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 b340b44d..395c1843 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 39fc813e..bed36762 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 b7247be4..348d7535 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 228fc0ae..f3966941 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 b1e92811..e335c270 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 00000000..d917bb03 --- /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 00000000..194fe4e4 --- /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 00000000..64f4e49e --- /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 00000000..a951562b --- /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 00000000..fe7e573d --- /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 00000000..1812f879 --- /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 7ab3c0ec..7664e7a7 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 9a7e3026..a7df2926 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 9117d19f..05bcdc49 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 dcd5d154..3b00fc57 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 c7a51507..2f252389 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 d0e41561..9db6b275 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 b8e51031..b9c9efa5 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 ffac0a9e..4c2c54b4 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 f2b8db98..d347c346 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 e5cbb2a3..0d37693f 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 2cadda95..18f8e6cf 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 1f7110a5..c974e633 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 f6c56526..d1c5ccb8 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 923ceecc..8f5a0e94 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 36389f0e..cf1f223b 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 df2b9007..7dd0c1d6 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 a1e212ae..68b0fe92 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 32cf3580..fbf8394f 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 98108d39..c9b2a9cb 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 062f5a11..a75aa016 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 b8d34055..c21e959b 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 92b0f796..8d90203f 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 805c672f..164b4c7e 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 44884649..ea52c88e 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 729dee3d..02b50c78 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 00000000..a7144eda --- /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 d978f522..32137a75 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 23399664..8b308947 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 d4903360..4e6d802b 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 89cfda1c..60ef8568 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 3b4ce9d5..1829bf01 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 e0aa1e8c..015eff40 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 63f9ac74..223c706b 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 601e8774..9a0eda8a 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 c1425b85..ce119914 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 6581ed2c..7a0990ef 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 163abe72..3d6152c9 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 44156b97..3ea66e30 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 a1594eb4..34e8136f 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 d99f3c94..46c9503a 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 fc332306..70275c4f 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 1bd44297..f01a5bba 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 ce5fa47c..89f2029c 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 c8eb7834..378b64f1 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 87fb8085..f30d5334 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 fb5c50a7..95678329 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 08f05929..69d428b2 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 2a6a82e3..af5ebfc4 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 74a76e70..c96d2be5 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 37d18d7a..7a1a989b 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 6384406e..91f7e7fd 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 d1c192e5..7c26baaf 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 1cd73e6e..458787bd 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 7d161822..e3bfeeb4 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 fa6ffc3b..1bf81ed2 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 d5bc9681..e6d3b96f 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 6a7e25d3..fdaf4a82 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 0c9ad4a8..e494918a 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 7d5edfaa..126d7160 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 d4970f5a..27a0e060 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 d51bf8aa..debe2bf5 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 22db26b1..087d1452 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 888e77b7..83dfce65 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 a0ebd8d9..d656bb59 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 7c06e13b..dda9afa3 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 04f26eb5..b5f5fe3d 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 5d1bdc19..9624901d 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 7761d62f..1c2e6afe 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 273c897c..2506f9cb 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 a6f92934..42bb566b 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 2f4df397..0c139220 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 47469ff9..f721013e 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 8bc250fb..75797aca 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 c253e272..0ff6ffba 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 04369cf0..8f6a48a9 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 67f7c9ad..c802db62 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 e1a42c83..ac43adea 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 2a3d7558..62872f3e 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 5502e81a..b28678f4 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 7756e0e4..824de1ef 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 6a0a13fd..d0dec03d 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 36f59c7b..fd190950 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 3835b515..e1d7b504 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 bf010b50..a88041c0 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 2d2e4627..70a24f9d 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 12c1df99..22b13db9 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 27a1e921..ecd02d87 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 74c95606..9ca62f69 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 fac80e07..215e9288 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 37ddfe39..f5771623 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 d8f9403c..b64279d7 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 d63acc4d..275450fd 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 23327276..5b75e3c2 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 e3eb691e..9b8f419b 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 e062c7d2..68b9aee7 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 738cd72c..be52e1f8 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 3ed8d238..9502d654 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 2a60957a..31a55ea3 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 0fda569d..d40dd45c 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 9dd69f66..0a380734 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 9e0fd76f..9f2391c8 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 00000000..fe1955f4 --- /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 00000000..9a804876 --- /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 e21cdc13..d35dbd76 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 edc9beb5..d67a8e91 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 16b6dace..427b744a 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 15e0befd..e7d83847 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 81c16337..b6aec18f 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 c3b0cda2..541dedc2 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 46198dca..f5f3dfe1 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 4c0ad519..e0da5e0e 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 cfbdaec2..65bf8ac0 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 c56518f6..38e45afa 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 92cd5dae..60e279c7 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 a9281b46..fc88fc4a 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 e8528fdf..6fad374b 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 a93408b4..ed61c773 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 ee44a969..2e9817c2 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 41ddd2d8..23f17b29 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 9989da43..2eafc409 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 57fc4bca..39311a8c 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 27574655..f1e094c3 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 46735682..13bfcaa0 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 0e008454..14c1d918 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 a5f8ae41..f31f4f27 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 dbb87b64..d8ff30b2 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 00c00e4c..3df616ab 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 09f41991..5aa9bfa1 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 b4a7ad3d..f8192e5c 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 c03ce284..60917e31 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 e744cde1..453f8b4c 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 1f07f199..a05b6c67 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 f50f36f7..5e96dfed 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 4b92e3c8..257a8f39 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 76d0562a..cbcd0a9e 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 e8b0183c..ec1e6ae1 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 5749fbb7..517ee64f 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 ee09978f..5e5f487a 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 00000000..911cb390 --- /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